Atomic типы данных
Классы java.util.concurrent.atomic выполняют одиночные обновления общего
состояния атомарно. Они полезны для счётчиков, флагов и ссылок, когда инвариант
умещается в одну переменную и блокировка не нужна.
Основные классы
| Группа | Примеры |
|---|---|
| скаляры | AtomicInteger, AtomicLong, AtomicBoolean |
| ссылки | AtomicReference, AtomicStampedReference |
| массивы | AtomicIntegerArray, AtomicReferenceArray |
| поля | AtomicIntegerFieldUpdater |
| конкурентные счётчики | LongAdder, LongAccumulator |
Стандартного AtomicDouble нет. Значение double можно хранить как биты в
AtomicLong, обновлять через AtomicReference<Double> или применять
DoubleAdder, если нужен конкурентный накопитель без атомарного снимка каждой
операции.
Compare-and-set
CAS записывает новое значение, только если текущее равно ожидаемому.
AtomicInteger balance = new AtomicInteger(10);
int updated = balance.updateAndGet(current -> {
if (current == 0) throw new IllegalStateException("empty");
return current - 1;
});Упрощённо обновление повторяется в цикле:
int current;
int next;
do {
current = balance.get();
next = current - 1;
} while (!balance.compareAndSet(current, next));При конкуренции CAS может несколько раз проиграть и повторить вычисление.
Поэтому функция в updateAndGet не должна выполнять внешний необратимый
эффект: она может быть вызвана больше одного раза.
Видимость и ограничения
Операции get/set атомарных классов имеют volatile-подобные гарантии
видимости. Подробная связь описана в happens before.
Атомарная переменная не защищает инвариант из нескольких значений:
проверка двух счетов и перевод между ними требуют lock, immutable state в
одном AtomicReference или другой координации.
ABA-проблема: значение меняется A → B → A, и обычный CAS не замечает
промежуточного состояния. Версию можно хранить в AtomicStampedReference.
LongAdder снижает конкуренцию, распределяя обновления по ячейкам. sum()
не является атомарным снимком относительно одновременных записей.
Вопросы для собеседования
- Почему
i++не атомарен? Это чтение, вычисление и запись. - Что делает CAS? Условно заменяет значение при совпадении с ожидаемым.
- Есть ли
AtomicDouble? Нет; выбор зависит от требуемой семантики. - Когда
LongAdderлучшеAtomicLong? При частых конкурентных прибавлениях и редком чтении точной суммы. - Что такое ABA? Возврат значения к A скрывает промежуточное изменение.
- Может ли atomic заменить транзакцию нескольких полей? Нет, если поля не объединены в одно атомарно заменяемое состояние.
- Почему updater-функция должна быть без побочных эффектов? CAS-цикл может выполнить её повторно.