В чем разница между ConcurrentHashMap и Collections.synchronizedMap (Map)? - PullRequest
571 голосов
/ 04 февраля 2009

У меня есть карта, которая должна быть изменена несколькими потоками одновременно.

Кажется, в Java API есть три разных реализации синхронизированных карт:

  • Hashtable
  • Collections.synchronizedMap(Map)
  • ConcurrentHashMap

Насколько я понимаю, Hashtable - это старая реализация (расширяющая устаревший класс Dictionary), которая позже была адаптирована для соответствия интерфейсу Map. Хотя синхронизирован, он, похоже, имеет серьезные проблемы с масштабируемостью и не рекомендуется для новых проектов.

Но как насчет двух других? В чем разница между картами, возвращаемыми Collections.synchronizedMap(Map) и ConcurrentHashMap с? Какой из них подходит к какой ситуации?

Ответы [ 18 ]

403 голосов
/ 04 февраля 2009

Для ваших нужд используйте ConcurrentHashMap. Это позволяет одновременно модифицировать карту из нескольких потоков без необходимости их блокирования. Collections.synchronizedMap(map) создает блокирующую карту, которая снижает производительность, хотя и обеспечивает согласованность (при правильном использовании).

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

224 голосов
/ 22 июля 2013
╔═══════════════╦═══════════════════╦═══════════════════╦═════════════════════╗
║   Property    ║     HashMap       ║    Hashtable      ║  ConcurrentHashMap  ║
╠═══════════════╬═══════════════════╬═══════════════════╩═════════════════════╣ 
║      Null     ║     allowed       ║              not allowed                ║
║  values/keys  ║                   ║                                         ║
╠═══════════════╬═══════════════════╬═════════════════════════════════════════╣
║Is thread-safe ║       no          ║                  yes                    ║
╠═══════════════╬═══════════════════╬═══════════════════╦═════════════════════╣
║     Lock      ║       not         ║ locks the whole   ║ locks the portion   ║        
║  mechanism    ║    applicable     ║       map         ║                     ║ 
╠═══════════════╬═══════════════════╩═══════════════════╬═════════════════════╣
║   Iterator    ║               fail-fast               ║ weakly consistent   ║ 
╚═══════════════╩═══════════════════════════════════════╩═════════════════════╝

Относительно механизма блокировки: Hashtable блокирует объект , а ConcurrentHashMap блокирует только корзину .

140 голосов
/ 04 февраля 2009

«Проблемы масштабируемости» для Hashtable точно так же присутствуют в Collections.synchronizedMap(Map) - они используют очень простую синхронизацию, что означает, что только один поток может получить доступ к карте одновременно.

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

ConcurrentHashMap использует очень сложные методы, чтобы уменьшить потребность в синхронизации и обеспечить параллельный доступ для чтения несколькими потоками без синхронизации, и, что более важно, обеспечивает Iterator, который не требует синхронизации и даже позволяет изменять карту во время interation (хотя это не гарантирует, будут ли возвращены элементы, которые были вставлены во время итерации).

33 голосов
/ 04 февраля 2009

ConcurrentHashMap предпочтительнее, когда вы можете его использовать - хотя для этого требуется как минимум Java 5.

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

Я нашел запись в блоге , которая воспроизводит таблицу из превосходной книги Параллелизм Java на практике , которую я полностью рекомендую.

Collections.synchronizedMap действительно имеет смысл, только если вам нужно обернуть карту некоторыми другими характеристиками, возможно, какой-то упорядоченной картой, например TreeMap.

32 голосов
/ 16 ноября 2014

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

Также еще одно отличие состоит в том, что ConcurrentHashMap не сохранит порядок элементов на карте, переданных внутрь. Он аналогичен HashMap при хранении данных. Нет гарантии, что порядок элементов сохранен. Хотя Collections.synchronizedMap() будет сохранять порядок элементов карты, переданной внутрь. Например, если вы передаете TreeMap в ConcurrentHashMap, порядок элементов в ConcurrentHashMap может не совпадать с порядком в TreeMap, но Collections.synchronizedMap() сохранит порядок.

Кроме того, ConcurrentHashMap может гарантировать, что не будет выброшено ConcurrentModificationException, пока один поток обновляет карту, а другой поток проходит через итератор, полученный из карты. Тем не менее, Collections.synchronizedMap() не гарантируется.

Существует один пост , который демонстрирует различия этих двух, а также ConcurrentSkipListMap.

12 голосов
/ 28 октября 2012

В ConcurrentHashMap блокировка применяется к сегменту, а не ко всей карте. Каждый сегмент управляет своей собственной внутренней хеш-таблицей. Блокировка применяется только для операций обновления. Collections.synchronizedMap(Map) синхронизирует всю карту.

11 голосов
/ 04 февраля 2009
  • Hashtable и ConcurrentHashMap не допускают null ключей или null значений.

  • Collections.synchronizedMap(Map) синхронизирует все операции (get, put, size и т. Д.).

  • ConcurrentHashMap поддерживает полный параллелизм получения и настраиваемый ожидаемый параллелизм для обновлений.

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

9 голосов
/ 04 февраля 2009

Вы правы насчет HashTable, вы можете забыть об этом.

В вашей статье упоминается тот факт, что хотя HashTable и синхронизированный класс-оболочка обеспечивают базовую безопасность потоков, позволяя только одному потоку одновременно получать доступ к карте, это не является «истинной» безопасностью потоков, поскольку многие составные операции все еще требуют дополнительной синхронизации, например:

synchronized (records) {
  Record rec = records.get(id);
  if (rec == null) {
      rec = new Record(id);
      records.put(id, rec);
  }
  return rec;
}

Однако не думайте, что ConcurrentHashMap - это простая альтернатива для HashMap с типичным блоком synchronized, как показано выше. Прочитайте эту статью, чтобы лучше понять ее тонкости.

9 голосов
/ 26 декабря 2017

Синхронизированная карта:

Синхронизированная карта также не сильно отличается от 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 безопасность потока достигается путем разделения всей карты на разные разделы на основе на уровне параллелизма и блокировки только определенной части вместо блокировки всей карты.

ConcurrentHashMap не допускает нулевые ключи или нулевые значения, в то время как синхронизированный HashMap допускает одно нулевые ключи.

Похожие ссылки

Link1

Link2

Сравнение производительности

7 голосов
/ 24 ноября 2011

Вот несколько:

1) ConcurrentHashMap блокирует только часть Map, но SynchronizedMap блокирует всю MAp.
2) ConcurrentHashMap имеет лучшую производительность по сравнению с SynchronizedMap и более масштабируемый.
3) В случае нескольких читателей и одного писателя лучшим выбором будет ConcurrentHashMap.

Этот текст из Разница между ConcurrentHashMap и хеш-таблицей в Java

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...