volatile решает не всё
volatile даёт видимость и запрет переупорядочивания (детали — в уроке про JMM), но НЕ делает составные операции атомарными. Классический пример: volatile int n; n++ — это read-modify-write из трёх шагов, и два потока легко затрут инкремент друг друга.
Идеальные кейсы volatile — одиночная запись/чтение флага или ссылки (флаг остановки, публикация готового объекта), а также volatile-поле в double-checked locking.
Атомарные классы
Для атомарных счётчиков и ссылок без блокировок есть пакет java.util.concurrent.atomic: AtomicInteger, AtomicLong, AtomicReference, AtomicBoolean. Их операции (incrementAndGet, getAndAdd, compareAndSet, updateAndGet) атомарны и потокобезопасны.
Это часто дешевле synchronized для простых операций, потому что не блокирует поток, а повторяет попытку.
AtomicInteger counter = new AtomicInteger();
counter.incrementAndGet(); // атомарно, без блокировки
counter.updateAndGet(v -> Math.min(v + 1, MAX));
CAS изнутри
Atomic-классы построены на CAS (compare-and-swap) — атомарной инструкции процессора «если значение равно ожидаемому, заменить на новое, иначе ничего». Алгоритм оптимистичен: прочитать текущее, вычислить новое, попытаться CAS; при неудаче (кто-то опередил) повторить цикл.
Так строят lock-free структуры: прогресс есть всегда без блокировок и без deadlock. Минусы — busy-retry под высокой конкуренцией и ABA-проблема: значение успело смениться A→B→A, CAS этого не заметит. Где это важно, используют AtomicStampedReference (значение + версия).
Контеншн и LongAdder
При высокой конкуренции один CAS-счётчик становится узким местом: потоки постоянно мешают друг другу и крутят retry-цикл, плюс кэш-линия с переменной «пинается» между ядрами (cache-line contention).
LongAdder решает это, разбивая счётчик на несколько ячеек (по потокам/полосам): потоки инкрементируют разные ячейки, а sum() складывает их. Для счётчиков с интенсивной записью LongAdder заметно быстрее AtomicLong; если же чаще читают точное значение — AtomicLong проще.
Как отвечать на собеседовании
Чётко разделите видимость (volatile) и атомарность (n++ небезопасно даже с volatile). Объясните CAS как оптимистичный lock-free цикл, назовите ABA и AtomicStampedReference, и скажите, когда брать LongAdder вместо AtomicLong (write-heavy счётчики, cache-line contention).