Как заблокировать ключ в ConcurrentHashMap

Я кэширую объект, созданный потоком, на карту. Создание объекта обходится дорого, поэтому я не хочу, чтобы несколько потоков выполнялись для создания объекта, потому что put() не вернулся. Как только поток попытается создать объект для этого ключа, другие потоки не должны пытаться создать объект, даже если размещение еще не завершено. Будет ли использование calculateIfAbsent() работать для получения «блокировки» на этом конкретном ключе? Если нет, есть ли другой способ добиться этого?


person dt94    schedule 06.08.2018    source источник
comment
Для правильной, эффективной и легкой библиотеки кэширования Java 8 вы можете взглянуть на Caffeine< /а>. Он абстрагирует то, о чем вы здесь беспокоитесь. Для Java ‹ 8 кэши Guava обеспечивают практически те же функции.   -  person Mick Mnemonic    schedule 07.08.2018
comment
В итоге я использовал Guava LoadingCache, как вы сказали, он делает все, чего я пытался достичь.   -  person dt94    schedule 14.08.2018


Ответы (1)


› Сработает ли использование calculateIfAbsent() для получения «блокировки» на этом конкретном ключе?

Да; per Javadoc для ConcurrentHashMap.computeIfAbsent(...):

Весь вызов метода выполняется атомарно, поэтому функция применяется не более одного раза для каждой клавиши.

Собственно, в этом и весь смысл метода.

Однако, чтобы быть ясным, замок не полностью специфичен для этого одного ключа; скорее, ConcurrentHashMap обычно работает, разбивая карту на несколько сегментов и используя одну блокировку для каждого сегмента. Это обеспечивает большую степень параллелизма и обычно является наиболее эффективным подходом; но вы должны знать, что это означает, что некоторые потоки могут блокироваться при создании вашего объекта, даже если они на самом деле не касаются одной и той же клавиши.

Если это проблема для вас, то другой подход заключается в использовании чего-то вроде ConcurrentHashMap<K, AtomicReference<V>>, чтобы отделить добавление записи карты от заполнения записи карты. (AtomicReference<V> не имеет метода computeIfAbsent, но в этот момент вы можете просто использовать обычную блокировку с двойной проверкой с комбинацией get() и synchronized.)

person ruakh    schedule 06.08.2018
comment
Или использовать кэширующую библиотеку? - person Mick Mnemonic; 07.08.2018