2014-02-06 2 views
1

Предположим, что есть ConcurrentHashMap и есть две темы.ConcurrentHashMap и его операции

Если оба потока считывают некоторые данные из одного и того же ведра, то в моем понимании говорится, что оба они могут читать этот ковш одновременно, поскольку CHM не блокирует операции чтения.

Но предположим, что одна нить пишет (put) в ведро. Затем может ли второй поток одновременно читать (get) из того же ведра или второй поток должен ждать завершения операции put?

Если бы Hashtable, то get должен будет дождаться завершения операции put. Но в случае ЧМ, как он будет себя вести?

+0

Я думаю, вам придется подождать, пока вы не напишете на хэш-карту, поскольку вы можете прочитать, что добавляет поток. – OmniOwl

+1

Если выполняется операция записи, все остальные потоки должны ждать. –

ответ

0

Не нужно спекулировать. source code for ConcurrentHashMap открыт, и любой может его прочитать. (Это JDK 8 build 128, первый кандидат на выпуск JDK 8.)

Вам не составит труда понять это, так как это всего лишь 6 300 строк. :-) Собственно, хорошая часть этого - комментарии, и большая часть кода идет на обработку крайних случаев. Прямые пути get() и put() не очень сложны и составляют всего несколько десятков строк кода.

Ваше понимание операций чтения (get(), contains()) верно; нет блокировки. Хеширование в ковше и поиск в ведре, если необходимо, просты, без блокировки. Наблюдение за памятью обеспечивается неустойчивыми чтениями. (На линиях 622-623 поля и next являются неустойчивыми.) Операции чтения выполняются одновременно с другими считываниями, а также с записью в тот же ковш.

Политика удаления и замены значений довольно проста в том, что головка ковша заблокирована, когда ведро выполняется поиск и изменение. См. Блок synchronized на линии 1117 от replaceNode. A put, который добавляет к существующему ковшу, аналогичен; см. блок synchronized по строке 1027 от putVal. Разумеется, эти операции блокируют другие потоки, пытающиеся удалить, заменить или добавить записи в это же ведро. Если значение находится в процессе замены, поток, получающий значение для этого ключа, будет видеть либо старое значение, либо новое значение, в зависимости от того, найдет ли поток чтения узел до или после того, как значение будет заменено записывать нить.

Существует специальный чехол для помещения первого элемента в ведро. На линиях 1018-1020, если putVal находит ведро пустым, оно создаст новый узел и CAS (сравните и поменяйте) его на место. Если это будет выполнено, операция будет завершена. Если два потока пытаются добавить узлы в одно и то же ведро более или менее одновременно, CAS для первого будет успешным, а CAS для второго не удастся. Но учтите, что этот код находится внутри цикла for (строка 1014). Поток, CAS которого провалился, просто обходит цикл и повторяет попытку. Фактически все остальные операции записи находятся в цикле. Общий подход заключается в том, что операции идут оптимистично, но проверяются для одновременных авторов. Если попытка оптимизма не удалась, операция повторяется и проходит через (возможно) другой путь, основанный на обновленном состоянии.

0

В Hastable параллельные операции будут блокировать всю коллекцию, но в ConcurrentHashMap будет заблокировано только одно ведро.

1

Привет, по моим сведениям ConcurrentHashMap позволяет считывать несколько считывателей одновременно без каких-либо блокировок. Это достигается путем разбиения карты на разные части на основе уровня параллелизма и блокировки только части Карты во время обновлений. Уровень параллелизма по умолчанию - 16, и, соответственно, карта разделена на 16 частей, и каждая часть управляется с помощью другой блокировки. Это означает, что 16 потоков могут работать на карте одновременно, пока они не будут работать на другой части Карты. Это делает ConcurrentHashMap высокой производительностью, несмотря на то, что безопасность потоков не повреждена. Хотя, это идет с оговоркой. Поскольку операции обновления, такие как put(), remove(), putAll() или clear() не синхронизированы, одновременное извлечение может не отражать самые последние изменения на карте.

Я надеюсь, что это поможет ..

1

Это из JavaDocs класса ConcurrentHashMap:

«операций Retrieval (включая ГЭТ), как правило, не блокируют, поэтому могут перекрываться с операциями обновления (в том числе и поместить удалить) извлечений отражают результаты самых последних завершенных операций обновления холдинговых после их появления»

0

из док:.

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

Операции поиска (включая получение) обычно не блокируются, поэтому перекрываются с операциями обновления (включая установку и удаление). Реквизиты отражают результаты последних завершенных операций по обновлению , удерживая их наступление. Для совокупных операций, таких как putAll и , четкие, одновременные изъятия могут отражать вставку или удаление только некоторых записей.Аналогичным образом, итераторы и перечисления возвращают элементы , отражающие состояние хеш-таблицы в какой-либо точке или после создания итератора/перечисления . Они не выбрасывают ConcurrentModificationException. Однако итераторы рассчитаны на , которые используются только по одному потоку за раз.

Итак, вы не должны ожидать, что операции будут синхронизироваться точно как Hashtable, но такая же (серия) операция является потокобезопасной. Второе выделенное предложение не подразумевает, но, на мой взгляд, настоятельно предлагает, что здесь происходит: put, т. Е. Не законченный, не будет блокировать get - get просто не увидит изменений.

Хотя я не работал себя через весь класс CHM, эта часть документации поддерживает мою гипотезу (взятую из OpenJDK 6)

static final class Segment<K,V> extends ReentrantLock implements Serializable { 
    /* 
    * Segments maintain a table of entry lists that are always 
    * kept in a consistent state, so can be read (via volatile 
    * reads of segments and tables) without locking. This 
    * requires replicating nodes when necessary during table 
    * resizing, so the old lists can be traversed by readers 
    * still using old version of table. 

Если обновление является «полным», кажется, не быть явно определены; как правило, как только новое ведро связано с списком ведер, я думаю. CHM также активно использует изменчивые поля для обеспечения того, чтобы потоки считывали последние списки в списке.

+0

'Выполняется, т. Е. Не закончено, не будет блокировать get - get будет просто не видеть изменений еще. '-Так что вы имеете в виду, что хотя один поток использует put() на CHM, другой может использовать get() на такое же ведро, но оно не увидит изменений, сделанных put()? –

Смежные вопросы