Concurrent коллекции
Обычные коллекции (ArrayList, HashMap) не потокобезопасны — если несколько потоков одновременно их меняют, можно получить повреждённые данные или ConcurrentModificationException. Для многопоточного кода есть специальные версии из java.util.concurrent.
Map<String, Integer> map = new ConcurrentHashMap<>(); // безопасно из нескольких потоков
map.put("счётчик", 1);
Подробнее про сам пакет java.util.concurrent — в теме «java.util.concurrent».
Копнуть глубже
ConcurrentHashMap — самая часто используемая. В отличие от старого Collections.synchronizedMap() (который блокирует всю карту целиком на каждой операции), ConcurrentHashMap блокирует только небольшую часть внутренней структуры — несколько потоков могут писать в разные участки одновременно.
CopyOnWriteArrayList — для случаев «читают часто, пишут редко». При каждой записи создаётся новая копия всего массива, зато чтение полностью без блокировок и без исключений при модификации во время обхода:
List<String> listeners = new CopyOnWriteArrayList<>();
// можно безопасно итерироваться, даже если кто-то добавит элемент во время обхода
Под капотом
ConcurrentHashMap использует комбинацию мелкой блокировки (lock striping — раньше) и CAS-операций (Compare-And-Swap, в современных версиях) — это позволяет нескольким потокам работать с разными частями карты по-настоящему параллельно, а не по очереди ждать одного общего замка. Подробнее про CAS — в теме «CAS».
CopyOnWriteArrayList оправдан только при редких записях — каждая запись копирует весь массив (O(n)), поэтому при частых изменениях он гораздо медленнее ConcurrentHashMap-подобных структур.
• разницу между `ConcurrentHashMap` и `CopyOnWriteArrayList` и когда что выбрать (если дошёл до 2-го слоя).