ConcurrentHashMap возвращает слабосогласованный итератор, почему мы должны его использовать в любом случае?
Я читаю книгу Java Concurrecny на практике. На стр. 85, раздел 5.2.1, рассказывается о ConcurrentHashMap и его преимуществах. Однако в одной части книги утверждают, что
итераторы, возвращаемые ConcurrentHashMap, слабо согласованы. Это означает, что этот итератор может допускать одновременное изменение, обходит элементы в том виде, в котором они существовали, когда был создан итератор, и может (но не гарантировано) отражать изменения в коллекции после создания итератора.
Из того, что я понимаю, весь смысл синхронизации в параллельных программах состоит в том, чтобы позволить потоку обращаться к общему ресурсу согласованным способом, тогда как ConcurrentHashMap на самом деле не выполняет этого. Тогда зачем вообще его использовать?
3 ответа
Дело в том, чтобы избежать синхронизации, когда она вам не нужна. Если вы не против увидеть новые элементы в некоторых случаях и не видеть их в других, используйте ConcurrentHashMap
Итератор может быть значительно дешевле, чем предотвращение добавления элементов другими потоками во время итерации или создание непротиворечивого снимка при его создании.
Так что да, когда вам нужна синхронизация и последовательный итератор, вам понадобится альтернатива - но когда вы этого не сделаете, вы можете воспользоваться более эффективным итератором, предоставляемым ConcurrentHashMap
,
ConcurrentHashMap
должен быть потокобезопасным, по контракту. Тем не менее, он не должен быть согласованным между потоками.
Когда вы перебираете ConcurrentHashMap
итератор получает копию хэш-карты в тот момент, когда вы ее запросили (и эта копия сделана в поточно-ориентированном режиме), и вы перебираете эту копию. И да, ничто не гарантирует вам, что пока вы перебираете эту копию, некоторые записи карты не будут удалены. Но записи карты, которые будут удалены таким образом, все еще существуют в вашем итераторе.
Все зависит от того, насколько сильная последовательность вам нужна. Если вам нужен сильный - мьютексы или другие подмножества операций предоставят то, что вам нужно.
Однако иногда вам нужны гораздо более слабые требования, но, например, вам нужно сказать свойство без блокировки, поскольку вам нужно гарантировать пропускную способность. С другой стороны, вам могут вообще не понадобиться итераторы или они имеют более слабые ограничения.
Так что, если все, что вам нужно, это карта, разделяемая между потоками ConcurrentHashMap
(скажем, место в театре человеку, который его заказывает) предоставит то, что вам нужно, и это может быть намного быстрее, если у вас много рабочих потоков, поскольку вы избегаете синхронизации мьютекса. С другой стороны, скорость достигается за счет затрат - представление, предоставляемое итератором, необязательно соответствует состоянию коллекции при его создании, и в нем могут отсутствовать некоторые элементы, созданные после слов (как правило, в многопроцессорных системах). happens-before
отношения сложны).
Редактировать: как принц Джон Уэсли указал, я думал о ConcurrentSkipListMap
пока вижу и пишу. ConcurrentHashMap
, Большинство пунктов остаются в силе, за исключением частей о том, что они не заблокированы (а также о том, что из этого вытекает, например о гарантированной пропускной способности и т. Д.).