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

Для чего нужна Atomic переменная?

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

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

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

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

# Atomic переменные в Java

Atomic переменные — это потокобезопасные примитивные типы данных из пакета java.util.concurrent.atomic, которые позволяют выполнять атомарные операции над переменными без явного использования синхронизации (synchronization).

Основное назначение

Atomic переменные нужны для:

  1. Безопасной работы с примитивами в многопоточной среде без locks
  2. Высокопроизводительного доступа (lock-free операции)
  3. Избежания race conditions при изменении данных
  4. Простоты кода (альтернатива synchronized блокам)

Проблема: Race Condition

Без Atomic (проблема)

public class Counter {
    private int count = 0;  // НЕ потокобезопасно
    
    public void increment() {
        count++;  // Три операции: read, increment, write
    }
    
    public int getCount() {
        return count;
    }
}

// В многопоточной среде результат будет неправильным
Counter counter = new Counter();
ExecutorService executor = Executors.newFixedThreadPool(2);
for (int i = 0; i < 1000; i++) {
    executor.submit(() -> counter.increment());
}
Thread.sleep(1000);
System.out.println(counter.getCount());  // Часто < 1000 вместо 1000

Почему проблема? Операция count++ состоит из трех шагов:

  1. Прочитать текущее значение (например, 5)
  2. Увеличить на 1 (6)
  3. Записать обратно (6)

Два потока могут одновременно прочитать 5, оба увеличат до 6, и результат будет 6 вместо 7.

Решение с AtomicInteger

С Atomic (решение)

public class Counter {
    private AtomicInteger count = new AtomicInteger(0);
    
    public void increment() {
        count.incrementAndGet();  // Атомарная операция
    }
    
    public int getCount() {
        return count.get();
    }
}

// Теперь результат всегда будет 1000
Counter counter = new Counter();
ExecutorService executor = Executors.newFixedThreadPool(2);
for (int i = 0; i < 1000; i++) {
    executor.submit(() -> counter.increment());
}
Thread.sleep(1000);
System.out.println(counter.getCount());  // Всегда 1000

Основные Atomic типы

1. AtomicInteger

AtomicInteger counter = new AtomicInteger(0);

counter.get();                // Получить значение
counter.set(5);               // Установить значение
counter.incrementAndGet();    // Увеличить и вернуть новое
counter.decrementAndGet();    // Уменьшить и вернуть новое
counter.getAndIncrement();    // Вернуть старое и увеличить
counter.addAndGet(10);        // Добавить и вернуть новое
counter.getAndAdd(10);        // Вернуть старое и добавить
counter.compareAndSet(5, 10); // Установить если равно 5

2. AtomicLong

AtomicLong timestamp = new AtomicLong(System.currentTimeMillis());
timestamp.incrementAndGet();
timestamp.addAndGet(1000);

3. AtomicBoolean

AtomicBoolean isRunning = new AtomicBoolean(true);

if (isRunning.get()) {
    isRunning.set(false);
}

4. AtomicReference

AtomicReference<String> reference = new AtomicReference<>("initial");
reference.set("updated");
String value = reference.get();

Compare-And-Swap (CAS) операция

Основной механизм, обеспечивающий потокобезопасность:

AtomicInteger counter = new AtomicInteger(5);

// Атомарно: если counter == 5, установить 10
boolean success = counter.compareAndSet(5, 10);
System.out.println(success);  // true

// Если снова попытаться с 5
boolean success2 = counter.compareAndSet(5, 15);
System.out.println(success2);  // false (значение уже 10)

Практический пример: Безопасный счетчик запросов

public class RequestCounter {
    private AtomicInteger totalRequests = new AtomicInteger(0);
    private AtomicInteger successfulRequests = new AtomicInteger(0);
    private AtomicInteger failedRequests = new AtomicInteger(0);
    
    public void recordRequest(boolean success) {
        totalRequests.incrementAndGet();
        
        if (success) {
            successfulRequests.incrementAndGet();
        } else {
            failedRequests.incrementAndGet();
        }
    }
    
    public void printStats() {
        System.out.println("Total: " + totalRequests.get());
        System.out.println("Success: " + successfulRequests.get());
        System.out.println("Failed: " + failedRequests.get());
    }
}

AtomicInteger vs synchronized

С synchronized (медленнее для контяции)

public class SyncCounter {
    private int count = 0;
    
    public synchronized void increment() {
        count++;
    }
    
    public synchronized int getCount() {
        return count;
    }
}

С AtomicInteger (быстрее)

public class AtomicCounter {
    private AtomicInteger count = new AtomicInteger(0);
    
    public void increment() {
        count.incrementAndGet();
    }
    
    public int getCount() {
        return count.get();
    }
}

Atomic быстрее благодаря:

  • CAS операциям (без полной блокировки)
  • Минимизации context switching
  • Hardware-level поддержке на современных процессорах

Практическое применение

1. Счетчик в веб-сервере

public class WebServer {
    private AtomicInteger activeConnections = new AtomicInteger(0);
    
    public void handleConnection() {
        activeConnections.incrementAndGet();
        try {
            // Обработка соединения
        } finally {
            activeConnections.decrementAndGet();
        }
    }
}

2. Флаг выключения

public class Worker implements Runnable {
    private AtomicBoolean shouldStop = new AtomicBoolean(false);
    
    @Override
    public void run() {
        while (!shouldStop.get()) {
            // Выполнить работу
        }
    }
    
    public void stop() {
        shouldStop.set(true);
    }
}

3. Кэширование с атомарным обновлением

public class Cache {
    private AtomicReference<Map<String, Object>> cache = 
        new AtomicReference<>(new HashMap<>());
    
    public Object get(String key) {
        return cache.get().get(key);
    }
    
    public void updateCache(Map<String, Object> newCache) {
        cache.set(newCache);
    }
}

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

✓ Потокобезопасность без synchronized (lock-free) ✓ Лучше перформанс в многопоточной среде ✓ Код более читаемый чем synchronized блоки ✓ Меньше risk deadlocks ✓ Hardware поддержка (CAS инструкции)

Недостатки

✗ Ограничены простыми типами ✗ Требуют понимания CAS операций ✗ Не подходят для сложной синхронизации ✗ Busy-waiting может быть неэффективен при высоком contention

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

Atomic подходит для:

  • Простых счетчиков и флагов
  • Высоконагруженных систем
  • Low-contention сценариев

Используй synchronized/Lock для:

  • Сложной синхронизации нескольких переменных
  • High-contention сценариев
  • Защиты целых блоков кода

Atomic переменные — незаменимый инструмент для написания эффективного многопоточного кода в Java.