Может ли concurrntHashMap одновременно гарантировать настоящую безопасность потоков и параллелизм?

Мы знаем, что ConcurrentHashMap может предоставлять одновременный доступ к нескольким потокам для повышения производительности, и внутри этого класса сегменты синхронизируются (я прав?). Вопрос в том, может ли эта конструкция гарантировать безопасность потока? Скажем, у нас есть более 30 потоков, которые обращаются и изменяют объект, отображаемый одним и тем же ключом в экземпляре ConcurrentHashMap, я думаю, им все равно придется выстраиваться в очередь для этого, не так ли?

Насколько я помню, в книге "Java Concurrency in Practice" говорится, что ConcurrentHashMap обеспечивает параллельное чтение и достойный уровень одновременного написания. в вышеупомянутом сценарии, и если мое предположение верно, производительность не будет лучше, чем при использовании статического API-интерфейса синхронизации коллекции?

Спасибо за уточнение, Джон


person J.E.Y    schedule 15.06.2011    source источник
comment
[Я помещаю свой ответ в комментарий, так как я не уверен на 100% в том, что я утверждаю.] Есть 2 причины, по которым ConcurrentHashMap может быть быстрее, чем оболочка: A) ConcurrentHashMap может читать без блокировка, B) он построен с параллельными атомарными переменными, которые используют более новые операции синхронизации кэша процессора (условное обновление), которые могут быть намного быстрее, чем старые методы синхронизации.   -  person toto2    schedule 15.06.2011


Ответы (5)


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

person Robin    schedule 15.06.2011
comment
+1 за фактическое чтение вопроса (в отличие от моего ответа, вздох). - person Chris Jester-Young; 15.06.2011
comment
... все еще нужно синхронизировать ... это неверно в большинстве случаев использования ConcurrentHashMap. Смотрите ответ Джеда. - person irreputable; 15.06.2011
comment
@irreputable: я думаю, что этот ответ относится к одновременным мутациям одного объекта значения карты (а не самой карты), с чем, конечно, ConcurrentHashMap вообще не поможет. Вопрос не очень ясен, но это звучит так, как будто он задается в первом абзаце. - person ColinD; 15.06.2011
comment
@Colin, вопрос в том, будут ли в большинстве случаев использования параллельной карты хранить в ней изменяемые объекты. мой опыт нет. люди, которые используют параллельную карту, сознательно или инстинктивно хранят в ней неизменяемые значения, и изменения публикуются путем обновления карты, а не значений. - person irreputable; 15.06.2011
comment
В вопросе говорится, что более 30 потоков будут обращаться к объекту и изменять его, поэтому они должны быть изменчивыми. - person Robin; 31.01.2013

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

Безопасная публикация означает, что любой (изменяемый) объект, полученный с карты, будет виден со всеми операциями записи в него до его размещения на карте. Однако это не поможет для публикации изменений, сделанных после его извлечения.

Однако параллелизм и безопасность потоков, как правило, трудно обосновать и исправить, если у вас есть изменяемые объекты, которые изменяются несколькими сторонами. Обычно вы должны заблокировать, чтобы сделать это правильно. Лучший подход часто состоит в том, чтобы использовать неизменяемые объекты в сочетании с ConcurrentMap условными методами putIfAbsent/replace и таким образом линеаризовать ваш алгоритм. Этот стиль без блокировки, как правило, легче обосновать.

person Jed Wesley-Smith    schedule 15.06.2011
comment
другой вариант, прочитайте значение, если оно нуждается в модификации clone()/скопируйте его, а затем, когда закончите, используйте CHM.replace(key, oldvalue, newValue). Конечно, ему нужен CAS-подобный цикл. Трудная часть - это сбой замены b/c, к которому должен быть готов алгоритм. ... Что требует внутреннего состояния и т. д. Я думаю, что блокировка + putIfAbsent кажется проще для большинства людей. - person bestsss; 15.06.2011

Вопрос в том, может ли эта конструкция гарантировать безопасность потока?

Гарантирует потокобезопасность карты; то есть доступ и обновления на карте имеют четко определенное и упорядоченное поведение при наличии нескольких потоков, выполняющих обновления одновременно.

Это гарантирует потокобезопасность объектов ключа или значения. И он не обеспечивает никакой формы синхронизации более высокого уровня.

Скажем, у нас есть более 30 потоков, которые обращаются и изменяют объект, отображаемый одним и тем же ключом в экземпляре ConcurrentHashMap, я думаю, им все равно придется выстраиваться в очередь для этого, не так ли?

Если у вас есть несколько потоков, пытающихся использовать один и тот же ключ, то их операции неизбежно будут до некоторой степени сериализованы. Это неизбежно.

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

person Stephen C    schedule 15.06.2011
comment
Я читаю JDK 6 и есть. Однако это не во внешнем get. Это get в хеш-таблицах сегментов. - person Stephen C; 15.06.2011
comment
изменению всего 2 месяца: mail.openjdk. java.net/pipermail/security-dev/2011-апрель/ - person irreputable; 15.06.2011
comment
@irreputable Java 6 фактически выполняет некоторую блокировку в случае, если переупорядочение JVM создает объект, но позже инициализирует изменчивое значение. На самом деле этого не может быть в Java 6, но никогда не удалялось (это было возможно в Java 5). При этом реализация Java 7 на самом деле является довольно приличным изменением общего класса. Но поскольку в Java 7 больше не будет проблемы с нулевым изменчивым значением (решается в 6), всегда изменчивы загрузки и нет блокировок. - person John Vint; 15.06.2011
comment
@John Новая модель памяти Java вступила в силу с Java 5, а не 6. Вы это имели в виду? - person irreputable; 15.06.2011
comment
@irreputable Вы правы в том, что Java 5 является введением в модель памяти. Я имел в виду, что когда была выпущена Java 6, проблема, которая заставляет блокировать чтение в Java 5, больше не возникала. - person John Vint; 15.06.2011
comment
@ Стивен, возврата к блокировкам никогда не бывает, на это есть даже цитата Дуга Ли, и он в основном сказал, что это подразумевает. (настоял Билл Пью) сбил многих разработчиков с толку. По сути, компилятору необходимо переупорядочить новое назначение Entry с volatile value, чтобы сделать это даже теоретически возможным. Немного странно, что поля volatile в c-tor не имеют той же спецификации. как окончательные: stackoverflow.com/questions/5002428/ - person bestsss; 15.06.2011

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

конструкция if(!map.contains(k))map.put(k,v); для putIfAbsent, например, не является потокобезопасной

и каждый доступ/модификация значения по-прежнему должен быть сделан потокобезопасным независимо

person ratchet freak    schedule 15.06.2011

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

person irreputable    schedule 15.06.2011