Как связаны атомарные операции, класс Atomic и ключевое слово volatile с синхронизацией в Java
Что проверяют
Различаете ли вы видимость и атомарность. Это ровно та пара понятий, на которой строится модель памяти.
Как отвечать
volatile решает задачу видимости: запись в такое поле сразу видна другим потокам, чтение не берётся из кэша, а компилятор не переставляет операции через него. Атомарности он не даёт: count++ это чтение, увеличение и запись, и два потока по-прежнему могут потерять инкремент.
Atomic-классы (AtomicInteger, AtomicLong, AtomicReference) решают задачу атомарности без блокировок. Внутри compare-and-swap: прочитали значение, посчитали новое, атомарно записали, если старое не изменилось, иначе повторили. Отсюда «неблокирующая» синхронизация: потоки не засыпают, а крутятся в цикле повторов.
synchronized и Lock дают и атомарность, и видимость, но ценой блокировки: поток засыпает, есть переключение контекста, возможна взаимная блокировка.
Как выбирать: одно поле-флаг, который только пишут и читают, это volatile; счётчик или ссылка с обновлением по текущему значению это Atomic; несколько связанных полей, которые нужно менять согласованно, это блокировка.
Полезная деталь про нагрузку: при сильной конкуренции AtomicLong проигрывает LongAdder, потому что тот раскладывает счётчик по ячейкам и складывает только при чтении.
Где спотыкаются
Считают volatile заменой синхронизации.
Не знают про compare-and-swap и не могут объяснить, почему Atomic работает без блокировок.
Применяют Atomic к нескольким полям сразу, теряя согласованность.
Не упоминают, что synchronized тоже гарантирует видимость, а не только взаимное исключение.