Пользовательская карта на основе ConcurrentHashMap и ReentrantReadWriteLock для перезагрузки

Гуру Java,

В настоящее время у нас есть HashMap<String,SomeApplicationObject>, который часто читается и иногда изменяется, и у нас возникают проблемы, связанные с тем, что во время модификации / перезагрузки операция чтения возвращает null, что неприемлемо.

Чтобы исправить это, у меня есть следующие варианты:

А. Используйте ConcurrentHashMap.

Это похоже на первый вариант, но операция, о которой мы говорим, reload() - означает clear(), за которым следует replaceAll(). Таким образом, если Map читается после clear() и до replaceAll(), он возвращает null, что нежелательно. Даже если я synchronize, это не решит проблему.

Б. Создайте другую реализацию на основе ReentrantReadWriteLock

Где бы я создал acqu Write Lock перед reload() операцией. Это кажется более подходящим, но я чувствую, что для этого должно быть что-то уже доступно, и мне не нужно изобретать велосипед.

Какой выход лучший?

ИЗМЕНИТЬ. Доступна ли уже какая-либо коллекция с такой функцией?


person Bharat Sinha    schedule 24.08.2012    source источник
comment
Ваши карты фактически неизменяемы, что делает их идеальной целью для публикации через volatile.   -  person Marko Topolnik    schedule 24.08.2012
comment
Фактически неизменяемый извините, я не очень хорошо понимаю immutable ... Не могли бы вы дать несколько советов ???   -  person Bharat Sinha    schedule 24.08.2012


Ответы (3)


Похоже, вы не уверены, как можно реализовать то, что предлагает Питер Лоури. Это могло выглядеть так:

class YourClass {
    private volatile Map<String, SomeApplicationObject> map;

    //constructors etc.

    public void reload() {
        Map<String,SomeApplicationObject> newMap = getNewValues();
        map = Collections.unmodifiableMap(newMap);
    }
}

Нет проблем с параллелизмом, потому что:

  • Новая карта создается через локальную переменную, которая по определению не является общей - getNewValues не нужно синхронизировать или атомарно.
  • Присвоение map атомарно
  • map является изменчивым, что гарантирует, что другие потоки увидят изменение
person assylias    schedule 29.08.2012

Поскольку вы перезагружаете карту, я бы заменил ее при перезагрузке.

Вы можете сделать это, используя изменчивую карту, которую вы полностью заменяете при ее обновлении.

person Peter Lawrey    schedule 24.08.2012
comment
не могли бы вы объяснить более подробно? Мне почему-то сложно объяснить, почему это сработает. Я понимаю, что volatile гарантирует видимость, но как будет работать составная операция, такая как clear, than replaceAll? Спасибо. - person Eugene; 24.08.2012
comment
Чтобы заменить всю карту, все, что вам нужно сделать, это назначить новую карту старой изменчивой ссылке в вашем поле, когда вы создадите новую копию. т.е. вы действительно заменяете все, даже саму карту, а не только ее содержимое. - person Peter Lawrey; 24.08.2012

Это звучит много как Гуавы _ 1_, хотя на самом деле это зависит от того, как вы заполняете карту и как вычисляете значения. . (Раскрытие: я делаю свой вклад в Guava.)

Настоящий вопрос заключается в том, можете ли вы указать, как вычислить свой SomeApplicationObject с учетом входных данных String. Судя по тому, что вы нам сказали, это может выглядеть примерно так ...

LoadingCache<String, SomeApplicationObject> cache = CacheBuilder.newBuilder()
   .build(
       new CacheLoader<String, SomeApplicationObject>() {
         public SomeApplicationObject load(String key) throws AnyException {
           return computeSomeApplicationObject(key);
         }
       });

Затем, когда вы захотите перестроить кеш, вы просто вызываете cache.invalidateAll(). Затем с помощью LoadingCache вы можете вызвать cache.get(key), и, если он еще не вычислил значение, оно будет пересчитано. Или, может быть, после вызова cache.invalidateAll() вы можете вызвать cache.loadAll(allKeys), хотя вам все равно потребуется иметь возможность загружать отдельные элементы за раз на случай, если между invalidateAll и loadAll возникнут какие-либо запросы.

Если это неприемлемо - если вы не можете загрузить одно значение по отдельности, вы должны загрузить их все сразу - тогда я бы продолжил подход Питера Лоури - сохраните volatile ссылку на карту (в идеале ImmutableMap), пересчитайте всю карту и назначьте новую карту ссылке, когда закончите.

person Louis Wasserman    schedule 24.08.2012
comment
SomeApplicationObject - это объект, который предварительно загружается из БД при запуске приложения и все время считывается с карты ... Иногда системный администратор может перезагружать значения. - person Bharat Sinha; 24.08.2012