В чём преимущество использования атомарных классов (AtomicInteger, AtomicReference) перед synchronized?

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

Ответ

Атомарные классы из пакета java.util.concurrent.atomic предоставляют возможность выполнять одиночные потокобезопасные операции над переменными без использования блокировок (lock-free), что часто приводит к более высокой производительности в условиях высокой конкуренции.

Принцип работы: В основе лежат аппаратные инструкции CAS (Compare-And-Swap), которые атомарно сравнивают и обновляют значение в памяти.

Пример: AtomicInteger vs synchronized

// С использованием AtomicInteger
AtomicInteger atomicCounter = new AtomicInteger(0);
atomicCounter.incrementAndGet(); // Атомарная операция без блокировок

// С использованием synchronized
class SynchronizedCounter {
    private int count = 0;
    public synchronized void increment() { count++; } // Блокировка всего объекта
    public synchronized int get() { return count; }
}

Ключевые преимущества атомарных классов:

  1. Отсутствие блокировок (Non-blocking): Потоки не приостанавливаются, а повторяют операцию (в цикле), если значение изменилось другим потоком. Это уменьшает накладные расходы на переключение контекста.
  2. Избегание deadlock: Невозможность взаимной блокировки, так как нет захвата нескольких мониторов.
  3. Гарантия видимости (happens-before): Все операции обеспечивают ту же семантику видимости изменений, что и volatile.
  4. Богатый API: Сложные атомарные операции в одну инструкцию:
    AtomicInteger atomic = new AtomicInteger(10);
    // Атомарно: прочитать, умножить на 2, сохранить, вернуть результат
    int doubled = atomic.updateAndGet(x -> x * 2); // Результат: 20

Когда использовать:

  • Для простых атомарных операций (инкремент, сравнение с обменом).
  • В качестве volatile-переменных с атомарными обновлениями.
  • Для реализации неблокирующих алгоритмов.

Ограничения: Атомарные классы не подходят для сложных составных операций, зависящих от нескольких переменных. В таких случаях нужны synchronized или java.util.concurrent.locks.