Что такое атомарные переменные и как они применяются в Java
Что проверяют
Понимаете ли вы, чем атомарность отличается от видимости и когда хватает атомика, а когда нужна блокировка. Следом часто спрашивают, почему volatile int count с count++ теряет инкременты и что такое CAS.
Как отвечать
Атомарные переменные лежат в пакете java.util.concurrent.atomic: AtomicInteger, AtomicLong, AtomicBoolean, AtomicReference, массивы вроде AtomicIntegerArray. Они дают две вещи сразу:
- атомарность одной операции:
incrementAndGet(),compareAndSet(),updateAndGet()выполняются целиком, другой поток не вклинится посередине; - видимость: чтение и запись имеют семантику
volatile, изменение сразу видно остальным потокам.
Работают они без блокировок, на процессорной инструкции compare-and-swap. На ARM64, где живёт почти весь Android, это пара LL/SC или инструкция CAS из расширения LSE. Операция вроде updateAndGet крутится в цикле: прочитать, вычислить, попытаться записать, при неудаче повторить.
Типичные случаи на Android: флаг однократного запуска через AtomicBoolean.compareAndSet(false, true), счётчик активных загрузок, генератор id. В Kotlin те же классы используются напрямую, а в корутинах похожая механика спрятана в MutableStateFlow.update { }, который тоже построен на CAS. Если нужно согласованно поменять несколько полей, атомики не помогут: здесь synchronized или Mutex из корутин. Для счётчика, который обновляют десятки потоков, быстрее LongAdder: он раскладывает значение по ячейкам и снижает конкуренцию.
Где спотыкаются
Путают volatile с атомарностью. volatile гарантирует видимость, но count++ остаётся тремя операциями, и инкременты теряются.
Собирают составную операцию из двух атомарных вызовов: if (counter.get() < max) counter.incrementAndGet(). Между проверкой и изменением успевает вклиниться другой поток, правильно через compareAndSet в цикле или updateAndGet.
Считают атомики всегда быстрее synchronized. При сильной конкуренции потоки раз за разом перезапускают CAS, и производительность падает. Для AtomicReference полезно знать ещё про ABA-проблему и AtomicStampedReference.