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

Ява Гуру,

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

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

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

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

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

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

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

РЕДАКТИРОВАТЬ Есть ли какая-либо коллекция уже доступна с такой функцией?

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 является изменчивым, что гарантирует, что другие потоки увидят изменения

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

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

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

Реальный вопрос заключается в том, можете ли вы указать, как рассчитать 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), заново вычислите всю карту и назначьте новую карту для ссылки, когда вы закончите.

Другие вопросы по тегам