← Назад к вопросам

Что такое Atomic переменная?

2.0 Middle🔥 242 комментариев
#JVM и память#Многопоточность и асинхронность

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое Atomic переменная?

Atomic переменная (атомарная переменная) — это тип переменной, операции чтения и записи над которой выполняются как единая, неделимая (атомарная) операция. Это гарантирует, что в многопоточной среде ни один поток не увидит промежуточное или некорректное состояние переменной в момент её модификации другим потоком.

Основная цель атомарных переменных — обеспечить потокобезопасность без использования блокировок (lock-free), что позволяет избежать проблем, связанных с традиционной синхронизацией: взаимоблокировок (deadlocks), инверсии приоритетов и снижения производительности из-за приостановки потоков.

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

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

Зачем нужны Atomic переменные?

В многопоточной среде простая операция инкремента counter++ не является атомарной. На уровне процессора она разбивается на три шага:

  1. Чтение текущего значения из памяти.
  2. Увеличение значения на единицу.
  3. Запись нового значения обратно в память.

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

// НЕПРАВИЛЬНО: Непотокобезопасный счётчик
public class UnsafeCounter {
    private int count = 0;
    public void increment() { count++; } // Потенциальная потеря данных
    public int get() { return count; }
}

Для решения этой проблемы с помощью synchronized потребуется блокировка:

// ПРАВИЛЬНО, но с блокировкой
public class SynchronizedCounter {
    private int count = 0;
    public synchronized void increment() { count++; }
    public synchronized int get() { return count; }
}

Атомарные переменные решают ту же задачу, но более эффективно, используя аппаратную поддержку процессора (инструкции типа CAS — Compare-And-Swap).

Реализация в Java (java.util.concurrent.atomic)

В Java пакет java.util.concurrent.atomic предоставляет классы для работы с атомарными переменными.

import java.util.concurrent.atomic.AtomicInteger;

// Потокобезопасный счётчик без блокировок
public class AtomicCounter {
    private AtomicInteger count = new AtomicInteger(0);

    public void increment() {
        // Атомарно увеличивает значение на 1
        count.incrementAndGet();
    }

    public int get() {
        // Атомарно возвращает текущее значение
        return count.get();
    }

    public void customUpdate() {
        // Атомарное обновление с использованием CAS
        int oldValue, newValue;
        do {
            oldValue = count.get(); // 1. Читаем текущее значение
            newValue = oldValue + 10; // 2. Вычисляем новое
            // 3. Пытаемся записать newValue, если текущее значение всё ещё равно oldValue
        } while (!count.compareAndSet(oldValue, newValue));
    }
}

Наиболее часто используемые атомарные классы:

  • AtomicInteger / AtomicLong: Для целочисленных типов.
  • AtomicBoolean: Для булевых значений.
  • AtomicReference<V>: Для безопасного обновления ссылок на объекты.
  • AtomicIntegerArray / AtomicLongArray: Для массивов.
  • AtomicReferenceFieldUpdater<T,V>: Для атомарного обновления полей объектов.

Принцип работы: Compare-And-Swap (CAS)

CAS — это атомарная инструкция, предоставляемая большинством современных процессоров. Она выполняет следующие действия в одном неделимом цикле:

  1. Получает текущее значение переменной из памяти (expectedValue).
  2. Вычисляет новое значение (newValue).
  3. Если текущее значение в памяти всё ещё равно expectedValue, записывает newValue и возвращает true. В противном случае операция завершается неудачей и возвращает false.

На этом принципе построены все методы атомарных классов, такие как incrementAndGet(), compareAndSet(), accumulateAndGet().

// Псевдокод, иллюстрирующий логику CAS
public class SimulatedAtomicInteger {
    private int value;

    public synchronized boolean compareAndSet(int expectedValue, int newValue) {
        if (this.value == expectedValue) {
            this.value = newValue;
            return true;
        }
        return false;
    }
}

Преимущества и недостатки

Преимущества:

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

Недостатки и ограничения:

  • Проблема ABA: Поток может увидеть, что значение не изменилось (A -> B -> A), и принять это за отсутствие модификаций, хотя изменения были. Для ссылочных типов решается с помощью AtomicStampedReference или AtomicMarkableReference.
  • Сложность реализации составных атомарных операций над несколькими переменными. Для этого требуется дополнительная синхронизация или использование классов типа AtomicReference, хранящих неизменяемые объекты-контейнеры.
  • Активное ожидание (busy-waiting): В случае высокой конкуренции поток может многократно выполнять цикл CAS без прогресса, расходуя процессорное время.

Применение в Android-разработке

В Android атомарные переменные часто используются:

  • Для реализации потокобезопасных счётчиков, флагов и состояний.
  • В кеширующих механизмах для ленивой инициализации (AtomicReference).
  • В составе более сложных неблокирующих алгоритмов и структур данных.
  • Для обновления конфигураций или моделей данных в фоновых потоках с гарантией целостности.
// Пример на Kotlin с AtomicReference для синглтона
class SomeManager private constructor() {
    companion object {
        @Volatile private var instance: SomeManager? = null
        private val lock = AtomicReference<Unit?>(null)

        fun getInstance(): SomeManager {
            if (instance == null) {
                // Попытка захватить "блокировку" через CAS
                if (lock.compareAndSet(null, Unit)) {
                    try {
                        instance = SomeManager()
                    } finally {
                        lock.set(null)
                    }
                }
            }
            return instance!!
        }
    }
}

Таким образом, Atomic переменные — это мощный низкоуровневый инструмент для написания высокопроизводительного и потокобезопасного кода, который, однако, требует от разработчика глубокого понимания многопоточности и его внутреннего устройства.