Live Lock в ConcurrentHashMap

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

Концептуально вызов вызова выглядит следующим образом

final Map<String, Boolean> map = new ConcurrentHashMap<>();
map.computeIfAbsent("k1", k1 -> map.computeIfAbsent("k2", k2 -> Boolean.TRUE));

(около 3 мс процессора сгорело между компьютерами). К сожалению, я не могу придумать хороший модульный тест для последовательного воспроизведения проблемы.

тем не мение

есть другая живая блокировка, которая может дать некоторую подсказку, если вычислительная функция пытается удалить ключ, для которого она вызывается:

 final Map<String, Boolean> map = new ConcurrentHashMap<>();
 map.computeIfAbsent("k", k -> map.remove("k"));

в то время как 2-й пример является довольно запутанным использованием одновременной карты хэшей, он приводит к той же самой трассировке стека, что и первая активная блокировка, поэтому может оказаться полезным.

Любая помощь будет высоко оценена!

1 ответ

Решение

Хорошо ConcurrentHashMap.computeIfAbsent документация прямо говорит:

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

Акцент мой. внутренне computeIfAbsent синхронизируется с записью хеш-таблицы, которая содержит ваш ключ. У вас могут возникнуть неожиданные блокировки, когда вы изменяете другой ключ, который попадает в ту же запись, что и обрабатываемый в данный момент.

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

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