Обеспечивает ли volatile long потокобезопасность для инкремента из нескольких потоков?

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

Ответ

Нет, volatile long не обеспечивает потокобезопасность для операции инкремента (например, counter++).

Причина: Ключевое слово volatile гарантирует только две вещи:

  1. Видимость: Все потоки видят последнее записанное значение переменной.
  2. Запрет переупорядочивания: Операции чтения/записи volatile-переменной не переупорядочиваются компилятором или процессором.

Проблема: Инкремент — это составная операция (read-modify-write), которая не является атомарной:

// Операция counter++ разбивается на:
1. long temp = counter; // Чтение
2. temp = temp + 1;     // Модификация
3. counter = temp;      // Запись

Между шагами 1 и 3 другие потоки могут прочитать устаревшее значение и перезаписать результат, что приводит к потере обновлений.

Потокобезопасные альтернативы:

Класс Описание Когда использовать
AtomicLong Предоставляет атомарные операции (incrementAndGet(), getAndAdd()). Стандартный выбор для атомарного счетчика.
LongAdder Хранит сумму в нескольких ячейках, что уменьшает contention (состязание). В условиях высокой конкуренции множества потоков на запись.

Пример:

// С AtomicLong
AtomicLong atomicCounter = new AtomicLong(0);
atomicCounter.incrementAndGet(); // Атомарно и потокобезопасно

// С LongAdder
LongAdder adder = new LongAdder();
adder.increment(); // Оптимизировано для частых обновлений
long sum = adder.sum(); // Получение итогового значения