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

Как Atomic переменная решает проблему Race Condition

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

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

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

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

Atomic переменные и решение Race Condition

Atomic переменные — это классы из пакета java.util.concurrent.atomic, которые обеспечивают потокобезопасные операции над базовыми типами данных без использования явной синхронизации. Они решают проблему race condition через использование CAS (Compare-And-Swap) алгоритма на уровне процессора.

Проблема Race Condition

Race condition возникает, когда несколько потоков одновременно обращаются к одной переменной:

// Небезопасный код
private int counter = 0;

public void increment() {
    counter++; // Не атомарная операция!
}

Операция counter++ состоит из трёх шагов:

  1. Прочитать текущее значение (read)
  2. Увеличить на 1 (increment)
  3. Записать новое значение (write)

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

Решение с Atomic

private AtomicInteger counter = new AtomicInteger(0);

public void increment() {
    counter.incrementAndGet(); // Полностью потокобезопасно
}

Как работает AtomicInteger

Atomic переменные используют CAS (Compare-And-Swap) алгоритм:

public final int incrementAndGet() {
    for (;;) {
        int current = get();
        int next = current + 1;
        if (compareAndSet(current, next))
            return next;
    }
}

В цикле:

  1. Получаем текущее значение
  2. Вычисляем новое значение
  3. Пытаемся установить его атомарно (CAS)
  4. Если CAS успешен — выходим, если нет — повторяем

CAS на уровне процессора

CAS — это одна машинная инструкция процессора (не может быть прервана). Это гарантирует атомарность операции:

// Псевдокод CAS
if (currentValue == expectedValue) {
    currentValue = newValue;
    return true;
} else {
    return false;
}

Основные Atomic классы

// Для примитивов
AtomicInteger count = new AtomicInteger(0);
AtomicLong sum = new AtomicLong(0);
AtomicBoolean flag = new AtomicBoolean(false);

// Для объектов
AtomicReference<String> ref = new AtomicReference<>("initial");

// Для массивов
AtomicIntegerArray arr = new AtomicIntegerArray(10);

Практический пример: счётчик без синхронизации

public class Counter {
    private AtomicInteger value = new AtomicInteger(0);
    
    public void increment() {
        value.incrementAndGet();
    }
    
    public int getValue() {
        return value.get();
    }
}

// Использование в многопоточной среде
Counter counter = new Counter();
ExecutorService exec = Executors.newFixedThreadPool(10);

for (int i = 0; i < 10; i++) {
    exec.submit(() -> {
        for (int j = 0; j < 1000; j++) {
            counter.increment();
        }
    });
}

exec.shutdown();
exec.awaitTermination(1, TimeUnit.SECONDS);
System.out.println(counter.getValue()); // Всегда 10000

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

  • Производительность: быстрее, чем synchronized, особенно при низкой конкуренции
  • Простота: удобный API, нет необходимости в явной синхронизации
  • Отсутствие deadlock: нет монитора, невозможен deadlock
  • Scalability: лучше масштабируется при работе с несколькими потоками

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

  • Счётчики и флаги в многопоточных приложениях
  • Кэширование с ленивой инициализацией
  • Lock-free алгоритмы
  • Когда нужна высокая производительность с минимумом синхронизации

Atomic переменные — мощный инструмент для написания эффективного многопоточного кода, позволяющий избежать сложности и возможных deadlock'ов при использовании явной синхронизации.

Как Atomic переменная решает проблему Race Condition | PrepBro