На каком принципе основано неблокирующее поведение класса AtomicInteger в Java?

«На каком принципе основано неблокирующее поведение класса AtomicInteger в Java?» — вопрос из категории Java Core, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Неблокирующее поведение AtomicInteger основано на принципе Compare-And-Swap (CAS).

Как работает CAS: Это атомарная операция, выполняемая на уровне процессора. Она принимает три аргумента: адрес памяти (V), ожидаемое старое значение (A) и новое значение (B). Операция выполняется только если текущее значение по адресу V равно A. В этом случае оно заменяется на B. В противном случае (значение было изменено другим потоком) операция завершается неудачей.

Реализация в Java: Классы атомиков (AtomicInteger, AtomicLong и др.) используют метод compareAndSet (и другие, построенные на нём), который внутри вызывает нативные методы (часто через sun.misc.Unsafe), задействующие аппаратную поддержку CAS.

Пример и внутренний цикл:

AtomicInteger counter = new AtomicInteger(0);

// Метод incrementAndGet() реализован примерно так:
public final int incrementAndGet() {
    int current, next;
    do {
        current = get(); // 1. Читаем текущее значение
        next = current + 1; // 2. Вычисляем новое
    } while (!compareAndSet(current, next)); // 3. Пытаемся установить новое значение
    return next;
}

Преимущества CAS перед блокировками (synchronized, Lock):

  • Отсутствие блокировок: Потоки не приостанавливаются, что исключает дорогостоящие переключения контекста.
  • Высокая производительность при низкой и средней конкуренции.

Недостатки (проблемы CAS):

  • ABA-проблема: Поток может не заметить, что значение менялось с A на B и обратно на A. Решается использованием AtomicStampedReference.
  • Busy-waiting (активное ожидание): При высокой конкуренции потоки могут многократно повторять цикл CAS, бесполезно расходуя процессорное время.