Ответ
Цель: Обеспечить высокую производительность при конкурентных операциях чтения и записи за счет уменьшения области (гранулярности) блокировки.
Проблема Hashtable/synchronized HashMap: Они используют единую глобальную блокировку на весь объект карты. При одновременной работе нескольких потоков даже с разными ключами они выстраиваются в очередь, что убивает параллелизм.
Решение ConcurrentHashMap (до Java 8): Карта внутренне разделена на сегменты (Segment) — независимые подтаблицы. Каждый сегмент — это по сути небольшая хэш-таблица со своей собственной блокировкой.
Принцип работы (упрощенно):
- Ключ хэшируется.
- По хэшу определяется, какому сегменту принадлежит ключ.
- Для операции с этим ключом блокируется только его сегмент, а не вся карта.
- Другие потоки могут свободно работать с другими сегментами.
// Концептуальная иллюстрация структуры (реализация менялась)
ConcurrentHashMap<K, V> map;
// Внутри: массив сегментов (например, 16 штук по умолчанию)
Segment[] segments = new Segment[16];
// Каждый Segment содержит свой небольшой HashEntry[] и ReentrantLock
class Segment {
HashEntry[] table;
ReentrantLock lock;
// ... методы put, get для своей части данных
}
// Операция put блокирует только один сегмент
map.put(key, value); // Блокируется Segment[hash(key) % segments.length]
Эволюция в Java 8 и новее:
Модель с сегментами была заменена на более эффективную, использующую отдельные synchronized-блоки на головных узлах (первых узлах) бакетов (bucket) и CAS-операции (Compare-And-Swap). Однако логика осталась схожей: блокируется не вся таблица, а только небольшая ее часть (один бакет).
Ключевые преимущества подхода с мелкогранулярными блокировками:
- Высокий параллелизм при записи: Потоки, работающие с ключами из разных сегментов/бакетов, не блокируют друг друга.
- Неблокирующее чтение: Операции
get()и обход итератором (в большинстве случаев) не требуют блокировок, так как используютvolatile-поля и безопасную публикацию узлов, что делает их очень быстрыми. - Предсказуемая производительность: Конфликты блокировок сведены к минимуму.
Итог: Блокировка по сегментам/бакетам — это фундаментальный паттерн в ConcurrentHashMap, который позволяет ей превосходить по производительности синхронизированные аналоги в многопоточной среде, обеспечивая при этом потокобезопасность.