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