Атомарная операция выполняется целиком или не выполняется: другой поток не увидит промежуточного состояния. Обычные чтение и запись выровненного слова атомарны, а count++ — нет: это чтение, вычисление и запись.
CAS — аппаратная инструкция (lock cmpxchg на x86, LL/SC на ARM): CAS(addr, expected, new) атомарно сравнивает значение с ожидаемым и, если совпало, записывает новое; возвращает, удалось ли. Из неё строится любое атомарное обновление:
do {
old = value
new = f(old)
} while (!CAS(value, old, new))
Если между чтением и CAS другой поток изменил значение, CAS не пройдёт, и мы попробуем снова с новым значением. Никто не засыпает — это неблокирующий (lock-free) подход.
Где применяется: AtomicInteger, AtomicReference, ConcurrentHashMap (вставка в пустую корзину), очереди без блокировок, счётчики ссылок, реализация самих мьютексов (быстрый путь захвата свободного замка — один CAS).
Плюсы и минусы. Нет переключений контекста, нет дедлоков, поток, застрявший посреди операции, не держит остальных. Но при сильной конкуренции потоки крутятся в повторах и бьются за одну кэш-линию — производительность падает. Для горячих счетчиков в Java есть LongAdder: ячейки на поток, суммирование при чтении.
ABA. Поток прочитал A, отвлёкся; другой сменил A на B и обратно на A. CAS первого пройдёт, хотя состояние менялось — в lock-free стеке это может означать, что узел уже удалён и переиспользован. Решение — сравнивать пару (значение, версия): AtomicStampedReference, или сборщик мусора, который не даст переиспользовать узел, пока на него есть ссылка.
Тот же приём в распределённых системах: оптимистическая блокировка в базе (UPDATE ... SET balance = ?, version = version + 1 WHERE id = ? AND version = ? — если обновлено 0 строк, перечитать и повторить), условная запись в Redis (WATCH/MULTI), etcd и DynamoDB (conditional writes).