Получить доступ

Несколько потоков одновременно пишут в обычную HashMap. Чем это грозит?

JuniorКоллекции
Ещё спрашивают так:
  • Потокобезопасен ли HashMap?
  • Чем ConcurrentHashMap отличается от Collections.synchronizedMap?
Коротко

HashMap не потокобезопасен: параллельная запись ломает структуру — потерянные записи, неверный size, в старых версиях — зависание обхода.

  • Ошибка проявляется не сразу и далеко от места проблемы
  • Решение — ConcurrentHashMap; synchronizedMap проще, но одна блокировка на всё

А ещё Даже потокобезопасная карта не спасает составные операции «проверь, затем положи» — нужны compute/merge/putIfAbsent

Так подсказку видит пользователь sotto на живом собеседовании — за пару секунд, пока вы думаете вслух.

Подробно

HashMap не рассчитан на одновременную запись из нескольких потоков. Два потока могут одновременно вставлять в одну корзину или запустить расширение таблицы — и одна из записей потеряется, size() станет неверным. В Java 7 и раньше параллельное расширение могло зациклить список в корзине, и обход зависал навсегда.

Коварство в том, что исключения обычно нет: данные просто оказываются неверными, и это обнаруживается далеко от места проблемы.

Решения:

  • **ConcurrentHashMap** — спроектирован для параллельного доступа: чтение без блокировок, запись блокирует только конкретную корзину;
  • **Collections.synchronizedMap** — обёртка с одной блокировкой на всю карту: проще, но потоки ждут друг друга; итерировать нужно, вручную синхронизируясь на карте.

Отдельная ловушка — составные операции. Код «если ключа нет — положить» из двух вызовов не атомарен даже на потокобезопасной карте: между проверкой и вставкой вмешается другой поток. Нужны атомарные методы: putIfAbsent, computeIfAbsent, merge.

Пример

Map<String, Integer> counts = new ConcurrentHashMap<>();
// counts.put(word, counts.getOrDefault(word, 0) + 1); // гонка
counts.merge(word, 1, Integer::sum);                  // атомарно

Что хотят услышать

  • HashMap не потокобезопасен, последствия параллельной записи.
  • ConcurrentHashMap против synchronizedMap.
  • Неатомарность составных операций и атомарные методы.

Частые ошибки

  • Считать отсутствие исключения признаком корректной работы.
  • Полагаться на синхронизирующую обёртку при составных операциях.

Проверьте себя

Один верный вариант. Подумайте, потом откройте ответ.

Показать варианты и ответ
  • ✓Структура может быть повреждена: потерянные записи, неверный размер, в старых версиях — зависание при обходе
  • ✗Ничего: карта синхронизирована по умолчанию
  • ✗Будет брошено исключение при первой же параллельной записи
  • ✗Записи просто выполнятся медленнее

Что спросят дальше

  • Чем конкурентная реализация лучше синхронизирующей обёртки?
  • Почему в конкурентной карте запрещены null?

Разбор темы

Связанные вопросы

Закрытая бета

Подсказки на собеседовании — в реальном времени

sotto слышит вопрос интервьюера и показывает короткий ответ и план, как в блоке «Коротко». Оставьте контакт — пришлём доступ и промокод.

  • Промокод на 2 часа — сверх пробных 30 минут
  • Программа для Windows — скачать сразу, войти — через Яндекс ID или VK ID
  • Прямая связь с разработчиком: что улучшить — сделаем