Как работает ConcurrentHashMap
Что проверяют
Понимаете ли вы, за счёт чего карта потокобезопасна без глобальной блокировки, и знаете ли устройство после Java 8. Следом спрашивают, почему в ней нельзя хранить null и безопасен ли код map.put(k, map.get(k) + 1).
Как отвечать
Начните со сравнения. Hashtable и Collections.synchronizedMap держат один монитор на весь объект: любые два потока идут по очереди, даже читающие. ConcurrentHashMap блокирует гораздо мельче.
Дальше устройство в Java 8 и новее:
- массив бакетов: узлы
Nodeс volatile-полями значения и ссылки на следующий, ячейки таблицы читаются и пишутся атомарными операциями; get: без блокировок вообще;put: CAS в пустой бакет, иначеsynchronizedна голове бакета. Длинная цепочка превращается вTreeBin, красно-чёрное дерево;- ресайз: удвоение таблицы с переносом по частям, перенесённые бакеты помечаются
ForwardingNode, и другие писатели подключаются к работе; - счётчик: не одно поле, а базовое значение плюс ячейки, как у
LongAdder, поэтомуsize()иmappingCount()под нагрузкой дают оценку.
Про Java 7 хватит одной фразы: там были сегменты с ReentrantLock и параметр concurrencyLevel. Сейчас этот параметр лишь подсказка для начального размера.
Про null: если get вернул null, в конкурентной карте нельзя отличить «ключа нет» от «значение null», а проверка containsKey устареет к следующей строке. Поэтому null запрещён.
Для составных действий есть атомарные методы: merge(k, 1, Integer::sum), computeIfAbsent, compute. Счётчики под сильной конкуренцией лучше хранить так: computeIfAbsent(k, x -> new LongAdder()).increment(). Bulk-операции forEach, search, reduce с порогом параллелизма идут на общем ForkJoinPool.
Где спотыкаются
Рассказывают про 16 сегментов как про нынешнее устройство. Сегментов нет с Java 8, это устаревшая картина.
Думают, что потокобезопасная карта делает потокобезопасным любой код над ней. get и затем put это две операции, между ними влезет другой поток, и инкремент потеряется.
Ждут от итератора снимка. Он слабо согласованный: исключения не будет, но изменения во время обхода он может как увидеть, так и пропустить.