У меня есть карта, которая должна быть изменена несколькими потоками одновременно.
Кажется, в Java API есть три разные реализации синхронизированных карт:
Hashtable
Collections.synchronizedMap(Map)
ConcurrentHashMap
Насколько я понимаю, Hashtable
это старая реализация (расширяющая устаревший Dictionary
класс), которая позже была адаптирована под Map
интерфейс. В то время как это будет синхронизировано, по- видимому, имеют серьезные проблемы масштабируемости и не рекомендуются для новых проектов.
Но как насчет двух других? Каковы различия между картами, возвращаемыми Collections.synchronizedMap(Map)
и ConcurrentHashMap
s? Какой из них подходит к какой ситуации?
java
dictionary
concurrency
Henning
источник
источник
ConcurrentSkipListMap
качестве еще одной поточно-безопаснойMap
реализации. Предназначен для одновременной работы под нагрузкой с использованием алгоритма Skip List .Ответы:
Для ваших нужд используйте
ConcurrentHashMap
. Это позволяет одновременно изменять карту из нескольких потоков без необходимости их блокировки.Collections.synchronizedMap(map)
создает блокирующую карту, которая ухудшает производительность, хотя и обеспечивает согласованность (при правильном использовании).Используйте второй вариант, если вам нужно обеспечить согласованность данных, и каждый поток должен иметь обновленное представление карты. Используйте первое, если производительность критична, и каждый поток только вставляет данные на карту, а чтение происходит реже.
источник
Что касается механизма блокировки:
Hashtable
блокирует объект , аConcurrentHashMap
блокирует только ведро .источник
Hashtable
не является блокирующей частью карты. Посмотрите на реализацию. Он используетsynchronized
ключ без предоставленной блокировки, так что это в основном означает, что он блокирует целоеhashtable
в каждой операции.«Проблемы масштабируемости»
Hashtable
присутствуют точно так же, посколькуCollections.synchronizedMap(Map)
они используют очень простую синхронизацию, что означает, что только один поток может получить доступ к карте одновременно.Это не является большой проблемой, когда у вас есть простые вставки и поиски (если вы не делаете это очень интенсивно), но становится большой проблемой, когда вам нужно перебрать всю карту, что может занять много времени для большой карты - в то время как один поток делает это, все остальные должны ждать, если они хотят что-то вставить или найти.
В
ConcurrentHashMap
использует очень сложные методы , чтобы уменьшить потребность в синхронизации и позволяют параллельный доступ для чтения нескольких потоков без синхронизации и, что более важно, обеспечивает ,Iterator
что не требует синхронизации и даже позволяет карте быть изменены во время Интерактивного (хотя это не дает никаких гарантий или не элементы, которые были вставлены во время итерации, будут возвращены).источник
ConcurrentHashMap предпочтительнее, когда вы можете его использовать - хотя для этого требуется как минимум Java 5.
Он предназначен для масштабирования при использовании нескольких потоков. Производительность может быть незначительно ниже, когда к карте одновременно обращается только один поток, но значительно выше, когда несколько потоков одновременно обращаются к карте.
Я нашел запись в блоге, которая воспроизводит таблицу из превосходной книги Java Concurrency In Practice , которую я полностью рекомендую.
Collections.synchronizedMap имеет смысл на самом деле, только если вам нужно обернуть карту некоторыми другими характеристиками, возможно, какой-то упорядоченной картой, например TreeMap.
источник
Основное различие между этими двумя заключается в том,
ConcurrentHashMap
что блокируется только часть данных, которые обновляются, в то время как другая часть данных может быть доступна другим потокам. ОднакоCollections.synchronizedMap()
при обновлении все данные будут заблокированы, другие потоки смогут получить доступ к данным только после снятия блокировки. Если есть много операций обновления и относительно небольшое количество операций чтения, вы должны выбратьConcurrentHashMap
.Также еще одно отличие заключается в том,
ConcurrentHashMap
что не будет сохраняться порядок элементов, передаваемых в карте. Это похоже наHashMap
сохранение данных. Нет гарантии, что порядок элементов сохранен. ХотяCollections.synchronizedMap()
будет сохраняться порядок элементов карты, переданной в. Например, если вы передаете aTreeMap
вConcurrentHashMap
, порядок элементов вConcurrentHashMap
может не совпадать с порядком вTreeMap
, ноCollections.synchronizedMap()
будет сохранять порядок.Более того,
ConcurrentHashMap
может гарантировать, что не будетConcurrentModificationException
выброшено, пока один поток обновляет карту, а другой поток перебирает итератор, полученный из карты. ОднакоCollections.synchronizedMap()
это не гарантировано.Есть один пост, который демонстрирует различия этих двух, а также
ConcurrentSkipListMap
.источник
Синхронизированная карта:
Синхронизированная карта также не сильно отличается от Hashtable и обеспечивает аналогичную производительность в параллельных программах Java. Единственное различие между Hashtable и SynchronizedMap состоит в том, что SynchronizedMap не является устаревшим, и вы можете обернуть любую Карту, чтобы создать ее синхронизированную версию, используя метод Collections.synchronizedMap ().
ConcurrentHashMap:
Класс ConcurrentHashMap предоставляет параллельную версию стандартного HashMap. Это улучшение функциональности synchronizedMap, предоставляемой в классе Collections.
В отличие от Hashtable и Synchronized Map, он никогда не блокирует всю карту, вместо этого он разделяет карту на сегменты, и для них выполняется блокировка. Это работает лучше, если число потоков чтения больше, чем количество потоков записи.
ConcurrentHashMap по умолчанию разделен на 16 регионов и применяются блокировки. Этот номер по умолчанию можно установить при инициализации экземпляра ConcurrentHashMap. При настройке данных в определенном сегменте получается блокировка для этого сегмента. Это означает, что два обновления могут по-прежнему безопасно выполняться одновременно, если каждое из них влияет на отдельные сегменты, что сводит к минимуму конфликт блокировок и, таким образом, увеличивает производительность.
ConcurrentHashMap не генерирует исключение ConcurrentModificationException
ConcurrentHashMap не генерирует исключение ConcurrentModificationException, если один поток пытается изменить его, в то время как другой перебирает его
Разница между synchornizedMap и ConcurrentHashMap
Collections.synchornizedMap (HashMap) вернет коллекцию, которая почти эквивалентна Hashtable, где каждая операция модификации на Map заблокирована на объекте Map, тогда как в случае ConcurrentHashMap безопасность потока достигается путем разделения всей Map на другой раздел на основе уровня параллелизма и только блокировка определенной части вместо блокировки всей карты.
ConcurrentHashMap не допускает нулевые ключи или нулевые значения, в то время как синхронизированный HashMap допускает один нулевые ключи.
Похожие ссылки
Link1
Link2
Сравнение производительности
источник
Hashtable
иConcurrentHashMap
не разрешайтеnull
ключи илиnull
значения.Collections.synchronizedMap(Map)
синхронизирует все операции (get
,put
,size
и т.д.).ConcurrentHashMap
поддерживает полный параллелизм получения и настраивает ожидаемый параллелизм для обновлений.Как обычно, есть параллелизм - накладные расходы - компромиссы скорости. Вы действительно должны рассмотреть подробные требования параллелизма вашего приложения, чтобы принять решение, а затем протестировать свой код, чтобы увидеть, достаточно ли он хорош.
источник
В
ConcurrentHashMap
, блокировка применяется к сегменту вместо всей карты. Каждый сегмент управляет своей собственной внутренней хеш-таблицей. Блокировка применяется только для операций обновления.Collections.synchronizedMap(Map)
синхронизирует всю карту.источник
Вы правы
HashTable
, вы можете забыть об этом.В вашей статье упоминается тот факт, что хотя HashTable и синхронизированный класс-оболочка обеспечивают базовую безопасность потоков, позволяя только одному потоку одновременно обращаться к карте, это не является «истинной» безопасностью потоков, поскольку многие составные операции по-прежнему требуют дополнительной синхронизации. пример:
Однако не думайте, что
ConcurrentHashMap
это простая альтернатива дляHashMap
типичногоsynchronized
блока, как показано выше. Прочитайте эту статью, чтобы лучше понять ее тонкости.источник
Вот несколько:
1) ConcurrentHashMap блокирует только часть Map, но SynchronizedMap блокирует весь MAp.
2) ConcurrentHashMap имеет лучшую производительность по сравнению с SynchronizedMap и более масштабируемый.
3) В случае нескольких читателей и одного писателя ConcurrentHashMap является лучшим выбором.
Этот текст из разницы между ConcurrentHashMap и хеш-таблицей в Java
источник
Мы можем добиться безопасности потоков, используя ConcurrentHashMap и synchronizedHashmap и Hashtable. Но есть большая разница, если вы посмотрите на их архитектуру.
источник
ConcurrentHashMap
SynchronizedHashMap
источник
источник
ConcurrentHashMap оптимизирован для одновременного доступа.
Доступ не блокирует всю карту, но использует более детальную стратегию, которая улучшает масштабируемость. Существуют также функциональные улучшения, специально предназначенные для одновременного доступа, например, параллельные итераторы.
источник
Следует обратить внимание на одну важную особенность, отличную
ConcurrentHashMap
от предоставляемой ею функции параллелизма, а именно отказоустойчивый итератор. Я видел разработчиков, использующихConcurrentHashMap
только потому, что они хотят редактировать набор записей - ставить / удалять, перебирая его.Collections.synchronizedMap(Map)
не предоставляет отказоустойчивый итератор, но вместо этого он предоставляет отказоустойчивый итератор. Отказоустойчивые итераторы используют снимок размера карты, который нельзя редактировать во время итерации.источник
источник
В общем, если вы хотите использовать,
ConcurrentHashMap
убедитесь, что вы готовы пропустить «обновления»(т. Е. Печать содержимого HashMap не гарантирует, что он будет печатать обновленную карту), и использовать API-интерфейсы, например,
CyclicBarrier
для обеспечения согласованности всей вашей программы. жизненный цикл.источник
Метод Collections.synchronizedMap () синхронизирует все методы HashMap и эффективно сводит его к структуре данных, в которую одновременно может входить один поток, поскольку он блокирует каждый метод с помощью общей блокировки.
В ConcurrentHashMap синхронизация выполняется немного по-другому. Вместо того чтобы блокировать каждый метод с помощью общей блокировки, ConcurrentHashMap использует отдельную блокировку для отдельных сегментов, блокируя, таким образом, только часть карты. По умолчанию имеется 16 блоков, а также отдельные блокировки для отдельных блоков. Таким образом, уровень параллелизма по умолчанию равен 16. Это означает, что теоретически в любой момент времени 16 потоков могут получить доступ к ConcurrentHashMap, если все они собираются разделить сегменты.
источник
ConcurrentHashMap был представлен как альтернатива Hashtable в Java 1.5 как часть пакета для параллелизма. С ConcurrentHashMap у вас есть лучший выбор не только в том случае, если он может безопасно использоваться в параллельной многопоточной среде, но также обеспечивает лучшую производительность, чем Hashtable и synchronizedMap. ConcurrentHashMap работает лучше, потому что он блокирует часть Map. Это позволяет согласованные операции чтения и в то же время поддерживает целостность путем синхронизации операций записи.
Как реализован ConcurrentHashMap
ConcurrentHashMap был разработан как альтернатива Hashtable и поддерживает все функциональные возможности Hashtable с дополнительными возможностями, так называемым уровнем параллелизма. ConcurrentHashMap позволяет нескольким читателям читать одновременно без использования блоков. Это становится возможным благодаря разделению Карты на разные части и блокированию только части Карты в обновлениях. По умолчанию уровень параллелизма равен 16, поэтому карта разбита на 16 частей, и каждая часть управляется отдельным блоком. Это означает, что 16 потоков могут работать с картой одновременно, если они работают с разными частями карты. Это делает производительность ConcurrentHashMap высокой, а не снижает потокобезопасность.
Если вас интересуют некоторые важные функции ConcurrentHashMap и когда вам следует использовать эту реализацию Map - я просто поставлю ссылку на хорошую статью - Как использовать ConcurrentHashMap в Java
источник
Помимо того, что было предложено, я хотел бы опубликовать исходный код, связанный с
SynchronizedMap
.Чтобы сделать
Map
поток безопасным, мы можем использоватьCollections.synchronizedMap
оператор и ввести экземпляр карты в качестве параметра.Реализация
synchronizedMap
вCollections
, как показано нижеКак видите, входной
Map
объект обернутSynchronizedMap
объектом.Давайте копаться в реализации
SynchronizedMap
,Что
SynchronizedMap
можно суммировать как добавление одиночной блокировки к основному методуMap
объекта ввода . Все методы, защищенные блокировкой, не могут быть доступны нескольким потокам одновременно. Это означает, что обычные операции, такие какput
иget
могут выполняться одним потоком одновременно для всех данных вMap
объекте.Это делает
Map
поток объектов безопасным в настоящее время, но производительность может стать проблемой в некоторых сценариях.Это
ConcurrentMap
намного сложнее в реализации, мы можем обратиться к Построению лучшего HashMap для деталей. В двух словах, он реализован с учетом безопасности потоков и производительности.источник