Что такое атомарные операции и CAS?
- Как работает AtomicInteger?
- Что такое compare-and-swap?
- Что такое проблема ABA?
- Что такое lock-free алгоритмы?
CAS (compare-and-swap) — инструкция процессора «если в ячейке ещё A, запиши B» за один неделимый шаг. На ней строят атомарные счётчики и структуры без блокировок.
- Счётчик на CAS: прочитать, посчитать, попробовать записать; не вышло — повторить
- Нет засыпания потоков, но при сильной конкуренции много повторов
- ABA: значение сменилось A → B → A, и CAS этого не заметил; лечится версией
А ещё Тот же принцип в базе — оптимистическая блокировка: UPDATE ... WHERE version = ?
Подробно
Атомарная операция выполняется целиком или не выполняется: другой поток не увидит промежуточного состояния. Обычные чтение и запись выровненного слова атомарны, а 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).
Пример
AtomicInteger counter = new AtomicInteger();
counter.incrementAndGet(); // CAS-цикл внутри
AtomicReference<BigDecimal> balance = new AtomicReference<>(BigDecimal.ZERO);
balance.updateAndGet(b -> b.add(BigDecimal.TEN)); // f может вызваться несколько раз — без побочных эффектов!Что хотят услышать
- Определение атомарности и почему
count++не атомарен. - Как работает CAS и цикл повтора.
- Плюсы против блокировок и поведение при высокой конкуренции.
- ABA и версия; аналог в базе — оптимистическая блокировка.
Частые ошибки
- Делать побочные эффекты внутри функции для
updateAndGet— она может выполниться несколько раз. - Считать lock-free всегда быстрее блокировок.
Проверьте себя
Один верный вариант. Подумайте, потом откройте ответ.
Показать варианты и ответ
- ✓CAS: «если в ячейке ещё A, запиши B» одной неделимой инструкцией; на ней строят атомарные счётчики без блокировок
- ✗CAS захватывает ячейку памяти блокировкой ОС и усыпляет остальные потоки, пока владелец не запишет новое значение и не отпустит её
- ✗CAS всегда замечает смену A → B → A
- ✗При высокой конкуренции CAS никогда не повторяет попытки
Что спросят дальше
Связанные вопросы
Разбор собеседования с sotto
После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.