Как обеспечить корректную работу многопоточной логики в Java и какие коллекции использовать
Что проверяют
Понимаете ли вы, откуда берутся гонки и ошибки видимости, а не только помните классы из java.util.concurrent. Следом обычно дают код со счётчиком или кэшем на HashMap и просят найти ошибку.
Как отвечать
Начните с принципа: проблемы возникают только на общем изменяемом состоянии. Лучше его не иметь: неизменяемые объекты, данные одного потока, передача сообщений через очередь. Если оно есть, нужны атомарность и видимость по модели памяти Java (отношение happens-before).
Инструменты:
volatile: только видимость. Годится для флага остановки, ноcount++на таком поле остаётся гонкой;- атомики:
AtomicInteger,AtomicReference, для счётчиков под высокой конкуренциейLongAdder; synchronizedиReentrantLock: для составных операций, уReentrantLockестьtryLockс тайм-аутом;- высокоуровневое:
ExecutorService,CompletableFuture, с Java 21 виртуальные потоки черезExecutors.newVirtualThreadPerTaskExecutor().
Коллекции:
ConcurrentHashMap: основной выбор для общей мапы. Составные операции черезcomputeIfAbsent,merge,compute, они атомарны.nullв ключах и значениях запрещён;CopyOnWriteArrayList: много чтений и редкие записи, например список слушателей. Каждая запись копирует массив;BlockingQueue:ArrayBlockingQueueилиLinkedBlockingQueueдля схемы производитель и потребитель с ограничением размера;ConcurrentSkipListMap: потокобезопасная отсортированная мапа.
Vector, Hashtable и Collections.synchronizedList блокируют всё целиком, а итерацию по обёртке всё равно нужно синхронизировать вручную.
Где спотыкаются
Пишут «проверить, потом положить» на ConcurrentHashMap: if (!map.containsKey(k)) map.put(k, v). Каждая операция потокобезопасна, пара нет, нужен computeIfAbsent.
Считают, что volatile делает инкремент атомарным. Он гарантирует только видимость, чтение и запись остаются двумя шагами.
Берут CopyOnWriteArrayList для списка с частыми вставками и копируют массив на каждую запись. Или захватывают блокировки в разном порядке и ловят deadlock, который воспроизводится раз в неделю.