Каково поведение по умолчанию в java для одновременного hashmap - PullRequest
0 голосов
/ 12 мая 2018

Внутренне блокирует все строки и помечает каждый ключ, который будет удален? Так что, если другой поток захочет получить доступ к ключу, который собирается удалить, он обеспечит правильное поведение?

Или мы должны синхронизировать функцию очистки

synchronized (this) {
    myMap.clear();
}

Например, рассмотрим следующее

myMap = {1: 1, 2: 1, 3: 1}

//Thread1
myMap.clear()
//Thread2
myMap.compute(1, (k, v) -> {v == null ? 0 : k + 1})

Что происходит, когда thread1 выполняется первым, а thread2 хочет получить доступ к key1, но key1 еще не удален?

Является ли результат

{}

или

{1: 0}

Ответы [ 2 ]

0 голосов
/ 12 мая 2018

Совместно рассматривая два пункта из официальной документации , эта идея дает представление.

  1. Операции получения (включая получение) обычно не блокируются, поэтому могут перекрываться с операциями обновления (включая операции добавления и удаления)

  2. Для агрегатных операцийтакие как putAll и clear, одновременные извлечения могут отражать вставку или удаление только некоторых записей.

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

0 голосов
/ 12 мая 2018

Поведение clear() в этом контексте не определено 1 . Javadoc состояния:

"Для агрегатных операций, таких как putAll и clear, одновременный поиск может отражать вставку или удаление только некоторых записей."

Глядя на исходный код, clear() реализуется путем очистки каждого из сегментов по одному. Каждый сегмент заблокирован во время очистки, но на всей карте нет блокировки. Это означает, что другой поток может добавить записи в только что очищенный сегмент до того, как общий вызов clear() вернется.

Таким образом, на практике любой из предлагаемых вами результатов / вариантов поведения возможен в зависимости от размера карт, распределения ключей между сегментами, используемой вами версии Java и ... времени.


Внутренне ли он блокирует все строки и помечает каждый ключ для удаления?

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


Относительно этого:

synchronized (this) {
    myMap.clear();
}

Это не будет препятствовать другим элементам вставлять элементы во время выполнения clear(). Это просто остановит одновременную очистку двух потоков (выполняющих один и тот же код).

Если вы хотите гарантировать, что clear() очистит карту, вам нужно будет обернуть карту с помощью оболочки Collections.synchronizedMap и использовать ее последовательно. На практике это противоречит цели использования ConcurrentHashMap.


Дополнительный вопрос:

Так что потенциально может быть бесконечный цикл очистки, верно? Если другой поток продолжает добавлять элемент, размер карты всегда> 0, то поток, который пытается очистить, будет продолжать работать.

Неа. Там не будет бесконечного цикла. Метод clear() не учитывает размер карты.

На самом деле произойдет то, что вызов clear() вернется, и карта не обязательно будет пустой.


1 - Внимательно прочитав, я понял, что цитируемый javadoc не дает прямого ответа на вопрос. На самом деле, если вы посмотрите на «контракт» в Map.clear() javadoc, то там будет там , что после возврата вызова карта будет пустой. Это явно противоречит Javadoc для ConcurrentHashMap.clear() Javadoc, и явно противоречит тому, что на самом деле делает код.

...