Я кэширую объект, созданный потоком, на карту. Создание объекта обходится дорого, поэтому я не хочу, чтобы несколько потоков выполнялись для создания объекта, потому что put() не вернулся. Как только поток попытается создать объект для этого ключа, другие потоки не должны пытаться создать объект, даже если размещение еще не завершено. Будет ли использование calculateIfAbsent() работать для получения «блокировки» на этом конкретном ключе? Если нет, есть ли другой способ добиться этого?
Как заблокировать ключ в ConcurrentHashMap
Ответы (1)
› Сработает ли использование calculateIfAbsent() для получения «блокировки» на этом конкретном ключе?
Да; per Javadoc для ConcurrentHashMap.computeIfAbsent(...)
:
Весь вызов метода выполняется атомарно, поэтому функция применяется не более одного раза для каждой клавиши.
Собственно, в этом и весь смысл метода.
Однако, чтобы быть ясным, замок не полностью специфичен для этого одного ключа; скорее, ConcurrentHashMap
обычно работает, разбивая карту на несколько сегментов и используя одну блокировку для каждого сегмента. Это обеспечивает большую степень параллелизма и обычно является наиболее эффективным подходом; но вы должны знать, что это означает, что некоторые потоки могут блокироваться при создании вашего объекта, даже если они на самом деле не касаются одной и той же клавиши.
Если это проблема для вас, то другой подход заключается в использовании чего-то вроде ConcurrentHashMap<K, AtomicReference<V>>
, чтобы отделить добавление записи карты от заполнения записи карты. (AtomicReference<V>
не имеет метода computeIfAbsent
, но в этот момент вы можете просто использовать обычную блокировку с двойной проверкой с комбинацией get()
и synchronized
.)