Atomic

Атомарные классы в Java из пакета java.util.concurrent.atomic обеспечивают безопасное изменение данных в многопоточных средах без использования традиционных механизмов синхронизации, таких как блокировки (synchronized). Вместо этого они полагаются на низкоуровневые операции атомарного обновления, такие как compareAndSet (CAS), которые поддерживаются процессорами. Это позволяет избежать затрат, связанных с блокировками, и улучшает производительность.

AtomicInteger

Атомарные операции над int.

AtomicInteger counter = new AtomicInteger(5);
get()

Возвращает текущее значение int.

int value = atomicInt.get(); // value = 5
set(int newValue)

Устанавливает новое значение.

atomicInt.set(10); // Устанавливает значение 10
lazySet(int newValue)

Похоже на set(), но с возможной задержкой выполнения, что может быть полезно в оптимизации производительности.

atomicInt.lazySet(15); // Задержанное изменение значения
getAndSet(int newValue)

Атомарно устанавливает новое значение и возвращает старое значение.

int oldValue = atomicInt.getAndSet(20); // oldValue = 15, atomicInt = 20
compareAndSet(int expect, int update)

Сравнивает текущее значение с ожидаемым (expect) и, если они равны, обновляет значение на новое (update). Возвращает true, если обновление произошло, и false — если нет.

boolean success = atomicInt.compareAndSet(20, 25); // success = true, atomicInt = 25
getAndIncrement()

Атомарно увеличивает значение на 1 и возвращает старое значение.

int oldValue = atomicInt.getAndIncrement(); // oldValue = 25, atomicInt = 26
incrementAndGet()

Атомарно увеличивает значение на 1 и возвращает новое значение.

int newValue = atomicInt.incrementAndGet(); // newValue = 27
getAndDecrement()

Атомарно уменьшает значение на 1 и возвращает старое значение.

int oldValue = atomicInt.getAndDecrement(); // oldValue = 27, atomicInt = 26
decrementAndGet()

Атомарно уменьшает значение на 1 и возвращает новое значение.

int newValue = atomicInt.decrementAndGet(); // newValue = 25
getAndAdd(int delta)

Атомарно увеличивает значение на заданное delta и возвращает старое значение.

int oldValue = atomicInt.getAndAdd(5); // oldValue = 25, atomicInt = 30
addAndGet(int delta)

Атомарно увеличивает значение на заданное delta и возвращает новое значение.

int newValue = atomicInt.addAndGet(5); // newValue = 35
weakCompareAndSet(int expect, int update)

Похож на compareAndSet(), но может не гарантировать, что операция всегда будет успешной в конкурентной среде (в некоторых реализациях).

boolean success = atomicInt.weakCompareAndSet(35, 40); // success = true/false
AtomicLong

Атомарные операции над long.

AtomicLong atomicLong = new AtomicLong(100L);
get()

Возвращает текущее значение.

long value = atomicLong.get(); // value = 100L
set(long newValue)

Устанавливает новое значение.

atomicLong.set(150L); // Устанавливает значение 150L
incrementAndGet()

Атомарно увеличивает значение на 1 и возвращает новое значение.

long newValue = atomicLong.incrementAndGet(); // newValue = 302L
decrementAndGet()

Атомарно уменьшает значение на 1 и возвращает новое значение.

long newValue = atomicLong.decrementAndGet(); // newValue = 300L
compareAndSet(long expect, long update)

Сравнивает текущее значение с ожидаемым и, если они равны, обновляет значение на новое. Возвращает true, если обновление произошло.

boolean success = atomicLong.compareAndSet(200L, 300L); // success = true, atomicLong = 300L
AtomicBoolean

Атомарные операции над boolean.

AtomicBoolean atomicBoolean = new AtomicBoolean(true);
get()

Возвращает текущее значение boolean.

boolean value = atomicBoolean.get(); // value = true
set(boolean newValue)

Устанавливает новое значение.

atomicBoolean.set(false); // atomicBoolean = false
compareAndSet(boolean expect, boolean update)

Сравнивает текущее значение с ожидаемым и, если они равны, обновляет значение на новое. Возвращает true, если обновление произошло.

boolean success = atomicBoolean.compareAndSet(true, false); // success = true, atomicBoolean = false
AtomicReference

Атомарные операции над объектными ссылками. Полезен, когда необходимо безопасно обновлять ссылки на объекты в многопоточной среде.

AtomicReference<String> atomicReference = new AtomicReference<>("initial");
get()

Возвращает текущее значение.

String value = atomicReference.get(); // value = "initial"
set(T newValue)

Устанавливает новое значение.

atomicReference.set("updated"); // atomicReference = "updated"
compareAndSet(T expect, T update)

Сравнивает текущее значение с ожидаемым и, если они равны, обновляет значение на новое. Возвращает true, если обновление произошло.

boolean success = atomicReference.compareAndSet("updated", "final"); // success = true
AtomicIntegerArray

Массив атомарных целых чисел (int)

AtomicIntegerArray atomicArray = new AtomicIntegerArray(10); // массив из 10 элементов

int[] values = {1, 2, 3};
AtomicIntegerArray atomicArray = new AtomicIntegerArray(values);
get(int index)

Возвращает значение элемента массива по индексу.

int value = atomicArray.get(0); // Получение элемента с индексом 0
set(int index, int newValue)

Устанавливает новое значение элемента по индексу.

atomicArray.set(0, 5); // Установка значения 5 элементу с индексом 0
compareAndSet(int index, int expect, int update)

Сравнивает текущее значение с ожидаемым и, если они равны, обновляет значение на новое. Возвращает true, если обновление произошло.

boolean success = atomicArray.compareAndSet(0, 5, 15); // Если значение 5, установить 15
AtomicLongArray

Массив атомарных длинных целых чисел (long).

AtomicLongArray atomicArray = new AtomicLongArray(10); // Массив из 10 элементов

long[] values = {1L, 2L, 3L};
AtomicLongArray atomicArray = new AtomicLongArray(values);
AtomicReferenceArray

Массив атомарных ссылок.

AtomicReferenceArray<String> atomicArray = new AtomicReferenceArray<>(10); // Массив из 10 элементов

String[] values = {"A", "B", "C"};
AtomicReferenceArray<String> atomicArray = new AtomicReferenceArray<>(values);
AtomicMarkableReference

Атомарная ссылка на объект с дополнительной меткой (булевым значением). Этот класс позволяет безопасно обновлять ссылку на объект и связанную с ним метку в многопоточной среде, что делает его полезным для реализации структур данных, таких как списки, где требуется дополнительная информация о состоянии элемента (например, удален он или нет).

AtomicMarkableReference<String> atomicRef = new AtomicMarkableReference<>("Initial", false);
get()

Возвращает текущее значение ссылки и метки.

String reference = atomicRef.getReference(); // Получение ссылки
boolean mark = atomicRef.isMarked();         // Получение метки
set(T newReference, boolean newMark)

Устанавливает новое значение ссылки и метки.

atomicRef.set("Updated", true); // Установка новой ссылки и метки
compareAndSet(T expectReference, T updateReference, boolean expectMark, boolean updateMark)

Сравнивает текущее значение ссылки и метки с ожидаемыми значениями и, если они равны, устанавливает новые значения. Возвращает true, если обновление произошло.

boolean success = atomicRef.compareAndSet("Initial", "NewValue", false, true); // Если текущая ссылка "Initial" и метка false, обновляет на "NewValue" и true
AtomicStampedReference

Обеспечивает атомарную ссылку на объект вместе с «штампом» (целочисленным значением), представляющим версию или состояние объекта. Этот класс полезен для предотвращения проблем, связанных с потерей обновлений и условной синхронизацией в многопоточных приложениях.

AtomicStampedReference<String> atomicRef = new AtomicStampedReference<>("Initial", 0);
getReference()

Возвращает текущее значение ссылки.

String reference = atomicRef.getReference();
getStamp()

Возвращает текущий штамп.

int stamp = atomicRef.getStamp();
set(T newReference, int newStamp)

Устанавливает новое значение ссылки и штампа.

atomicRef.set("Updated", 1);
compareAndSet(T expectReference, T updateReference, int expectStamp, int updateStamp)

Сравнивает текущее значение ссылки и штампа с ожидаемыми значениями, и если они равны, устанавливает новые значения. Возвращает true, если обновление произошло.

boolean success = atomicRef.compareAndSet("Initial", "NewValue", 0, 1);
// Если текущая ссылка "Initial" и штамп 0, обновляет на "NewValue" и 1
Вопросы на собесе (8)
  1. Что такое атомарность?

    Это свойство операций, гарантирующее их выполнение как единое целое. В многопоточности атомарные операции завершаются полностью или не выполняются вовсе, предотвращая гонки состояний и обеспечивая целостность данных при параллельном доступе.

  1. Для чего нужны атомарные переменные?

    Атомарные переменные обеспечивают безопасное взаимодействие между потоками, предотвращая гонки данных и упрощая код за счет выполнения операций как неделимых. Они быстрее, чем традиционные методы синхронизации, так как не требуют блокировок, что повышает производительность многопоточных приложений.

  1. Как в атомиках хранится захватываемый объект?

    Хранится в виде ссылки.

  1. За счет чего достигается атомарность в AtomicInteger?

    Атомарность в AtomicInteger достигается с использованием неблокирующих алгоритмов, основанных на операции сравнения и обмена (Compare-And-Swap, CAS). Эта операция позволяет обновлять значение переменной только в том случае, если текущее значение соответствует ожидаемому, что предотвращает гонки данных без необходимости использования синхронизации.

  1. Чем AtomicInteger отличается от обычного int?

    AtomicInteger обеспечивает атомарные операции для безопасного изменения значения в многопоточной среде без блокировок, в отличие от обычного int, который может привести к состояниям гонки.

  1. Как атомики устроены под капотом?

    Реализованы с использованием низкоуровневых механизмов, предоставляемых JVM, включая операции с использованием compareAndSwap (CAS). Эти операции обеспечивают атомарность изменений, позволяя обновлять значения переменных без необходимости использования явных синхронизаций, таких как блокировки. Каждый атомический класс управляет внутренним состоянием через volatile поля, что гарантирует видимость изменений между потоками.

  1. Как compareAndSet работает под капотом?

    Метод получает текущее значение переменной из памяти и сохраняет его локально. Он сравнивает это значение с ожидаемым, переданным в параметре, и, если они совпадают, обновляет переменную новым значением атомарно, предотвращая изменения со стороны других потоков. Метод возвращает true при успешном обновлении и false, если текущее значение не совпадает с ожидаемым, позволяя повторить попытку обновления при необходимости.

  1. Являются ли чтение и запись атомарными операциями?

    Не являются атомарными операциями по умолчанию в большинстве языков программирования, включая Kotlin и Java.