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

Что такое Atomic в Java?

2.0 Middle🔥 141 комментариев
#JVM и управление памятью#Многопоточность

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

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

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

# Что такое Atomic в Java?

Atomic классы в Java — это специальные классы из пакета java.util.concurrent.atomic, которые обеспечивают потокобезопасные операции над переменными БЕЗ использования synchronized.

Краткое определение

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

Atomic класс — это контейнер для переменной, которая предоставляет потокобезопасные операции с использованием Compare-and-Swap (CAS) механизма вместо блокировок.

Полное объяснение

1. Проблема: Race Condition без Atomic

❌ БЕЗ Atomic — Race Condition

// Простой счётчик
class Counter {
    private int count = 0;
    
    public void increment() {
        count++;  // ❌ НЕБЕЗОПАСНО в многопоточной среде!
    }
    
    public int getCount() {
        return count;
    }
}

// count++ это НЕ атомарная операция! Она состоит из трёх шагов:
// 1. Читаем текущее значение: temp = count
// 2. Увеличиваем: temp = temp + 1
// 3. Записываем обратно: count = temp

// Пример race condition:
// Thread 1: read count=0
// Thread 2: read count=0
// Thread 1: write count=1
// Thread 2: write count=1
// Результат: count=1, хотя должен быть 2!

✅ С Atomic — Безопасно

import java.util.concurrent.atomic.AtomicInteger;

// Потокобезопасный счётчик
class AtomicCounter {
    private AtomicInteger count = new AtomicInteger(0);
    
    public void increment() {
        count.incrementAndGet();  // ✅ Атомарная операция
    }
    
    public int getCount() {
        return count.get();
    }
}

// AtomicInteger.incrementAndGet() — это атомарная операция
// Она гарантирует, что операция выполнится полностью в одном потоке
// без промежуточных состояний

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

import java.util.concurrent.atomic.*;

// Для примитивных типов:
AtomicBoolean     // boolean значение
AtomicInteger     // int значение
AtomicLong        // long значение

// Для ссылочных типов:
AtomicReference<T>           // Ссылка на объект
AtomicReferenceArray<E>      // Массив ссылок
AtomicIntegerArray           // Массив int
AtomicLongArray              // Массив long

// Для обновляемых полей:
AtomicIntegerFieldUpdater<T>
AtomicLongFieldUpdater<T>
AtomicReferenceFieldUpdater<T,V>

3. Как работает Atomic: Compare-and-Swap (CAS)

// Атомарное обновление использует CAS операцию:
// CAS(address, expectedValue, newValue):
//   if (memory[address] == expectedValue) {
//       memory[address] = newValue
//       return true
//   } else {
//       return false
//   }

// Пример:
public class AtomicIntegerExample {
    private AtomicInteger count = new AtomicInteger(0);
    
    // incrementAndGet внутренне использует CAS
    public void increment() {
        boolean updated = false;
        while (!updated) {
            int current = count.get();           // Читаем текущее значение
            int next = current + 1;
            updated = count.compareAndSet(current, next);  // CAS операция
            // Если другой поток изменил значение, повторяем
        }
    }
}

4. Основные операции AtomicInteger

import java.util.concurrent.atomic.AtomicInteger;

AtomicInteger counter = new AtomicInteger(0);

// 1. Получить значение
int value = counter.get();  // Возвращает текущее значение

// 2. Установить значение
counter.set(5);  // Устанавливает в 5

// 3. Получить и установить атомарно
int oldValue = counter.getAndSet(10);  // Возвращает 5, устанавливает 10

// 4. Увеличить на 1
counter.incrementAndGet();      // Увеличить и вернуть новое значение
int previous = counter.getAndIncrement();  // Вернуть старое и увеличить

// 5. Уменьшить на 1
counter.decrementAndGet();      // Уменьшить и вернуть новое
counter.getAndDecrement();      // Вернуть старое и уменьшить

// 6. Добавить число
counter.addAndGet(5);           // Добавить 5, вернуть новое значение
counter.getAndAdd(5);           // Вернуть старое, добавить 5

// 7. Compare-and-Swap (CAS)
boolean success = counter.compareAndSet(10, 20);  // Если равно 10, то 20
System.out.println(success);    // true если успешно

// 8. Получить и обновить
int result = counter.updateAndGet(x -> x * 2);   // Умножить на 2

// 9. Слабые операции (faster but no volatile semantics)
counter.lazySet(30);            // Быстрее, но может быть задержка видимости

5. Практический пример: Многопоточный счётчик

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

class MultiThreadCounter {
    private AtomicInteger count = new AtomicInteger(0);
    
    public void increment() {
        count.incrementAndGet();
    }
    
    public int getCount() {
        return count.get();
    }
    
    public static void main(String[] args) throws InterruptedException {
        MultiThreadCounter counter = new MultiThreadCounter();
        ExecutorService executor = Executors.newFixedThreadPool(10);
        
        // Запустим 10 потоков, каждый увеличит счётчик 1000 раз
        for (int i = 0; i < 10; i++) {
            executor.submit(() -> {
                for (int j = 0; j < 1000; j++) {
                    counter.increment();
                }
            });
        }
        
        executor.shutdown();
        executor.awaitTermination(1, java.util.concurrent.TimeUnit.MINUTES);
        
        // Результат будет всегда 10000, благодаря AtomicInteger!
        System.out.println("Final count: " + counter.getCount());  // 10000
    }
}

// Сравни с synchronized:
class SynchronizedCounter {
    private int count = 0;
    
    public synchronized void increment() {
        count++;
    }
    
    public synchronized int getCount() {
        return count;
    }
}

// Atomic быстрее, потому что использует CAS вместо блокировок!

6. AtomicReference для объектов

import java.util.concurrent.atomic.AtomicReference;

class AtomicReferenceExample {
    // Атомарная ссылка на объект
    private AtomicReference<User> currentUser = new AtomicReference<>();
    
    public void setUser(User user) {
        currentUser.set(user);  // Потокобезопасно
    }
    
    public User getUser() {
        return currentUser.get();  // Потокобезопасно
    }
    
    // Замена атомарно
    public boolean compareAndSetUser(User expected, User newUser) {
        return currentUser.compareAndSet(expected, newUser);
    }
    
    public static void main(String[] args) {
        AtomicReference<String> ref = new AtomicReference<>("initial");
        
        // Получить и заменить
        String old = ref.getAndSet("new");
        System.out.println("Old: " + old);    // initial
        System.out.println("New: " + ref.get());  // new
        
        // CAS
        boolean updated = ref.compareAndSet("new", "updated");
        System.out.println("Updated: " + updated);  // true
    }
}

7. AtomicIntegerArray для массивов

import java.util.concurrent.atomic.AtomicIntegerArray;

class AtomicArrayExample {
    private AtomicIntegerArray array = new AtomicIntegerArray(5);
    
    public static void main(String[] args) {
        AtomicIntegerArray array = new AtomicIntegerArray(3);
        
        // Установить значения
        array.set(0, 10);
        array.set(1, 20);
        
        // Получить значение (потокобезопасно)
        System.out.println(array.get(0));  // 10
        
        // Увеличить элемент атомарно
        array.incrementAndGet(0);
        System.out.println(array.get(0));  // 11
        
        // Добавить значение
        array.addAndGet(1, 5);
        System.out.println(array.get(1));  // 25
        
        // CAS
        boolean success = array.compareAndSet(0, 11, 100);
        System.out.println(success);  // true
    }
}

8. Atomic vs Synchronized

                  Atomic            Synchronized
────────────────────────────────────────────
Механизм          CAS (lock-free)   Блокировка (lock)
Производство      Обычно быстрее     Медленнее при конфликтах
Contention        Хорошо при        Плохо при высоком
                  высоком           конфликте
Сложность         Несложный          Простой
Контроль          Низкоуровневый     Высокоуровневый
Типы операций     Простые            Сложные блоки

Когда использовать:
Atomic    → Простые переменные (счётчики, флаги)
Sync      → Сложные блоки кода, множество операций

9. Производительность: Atomic vs Synchronized

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

class PerformanceComparison {
    
    // Тест с AtomicInteger
    static void testAtomic() throws InterruptedException {
        AtomicInteger counter = new AtomicInteger(0);
        ExecutorService executor = Executors.newFixedThreadPool(10);
        
        long start = System.nanoTime();
        
        for (int i = 0; i < 10; i++) {
            executor.submit(() -> {
                for (int j = 0; j < 1_000_000; j++) {
                    counter.incrementAndGet();
                }
            });
        }
        
        executor.shutdown();
        executor.awaitTermination(1, TimeUnit.MINUTES);
        
        long duration = System.nanoTime() - start;
        System.out.println("AtomicInteger time: " + duration / 1_000_000 + "ms");
    }
    
    // Тест с synchronized
    static void testSynchronized() throws InterruptedException {
        int[] counter = {0};
        ExecutorService executor = Executors.newFixedThreadPool(10);
        
        long start = System.nanoTime();
        
        for (int i = 0; i < 10; i++) {
            executor.submit(() -> {
                for (int j = 0; j < 1_000_000; j++) {
                    synchronized(counter) {
                        counter[0]++;
                    }
                }
            });
        }
        
        executor.shutdown();
        executor.awaitTermination(1, TimeUnit.MINUTES);
        
        long duration = System.nanoTime() - start;
        System.out.println("Synchronized time: " + duration / 1_000_000 + "ms");
    }
}

// Результат:
// AtomicInteger time: 300ms (примерно)
// Synchronized time: 1000ms (примерно)
// Atomic быстрее в 3-4 раза!

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

// ✅ ИСПОЛЬЗОВАТЬ Atomic когда:
class GoodAtomicUsage {
    private AtomicInteger pageViews = new AtomicInteger(0);     // Счётчик
    private AtomicLong lastUpdateTime = new AtomicLong(0);      // Время
    private AtomicReference<Config> config = new AtomicReference<>();  // Конфиг
    
    public void recordPageView() {
        pageViews.incrementAndGet();
    }
    
    public void updateConfig(Config newConfig) {
        config.set(newConfig);
    }
}

// ❌ НЕ ИСПОЛЬЗОВАТЬ Atomic когда:
class BadAtomicUsage {
    private AtomicInteger value1 = new AtomicInteger(0);
    private AtomicInteger value2 = new AtomicInteger(0);
    
    // ❌ Сложная операция, требующая нескольких атомарных действий
    public void transferValue() {
        // Это НЕ атомарно для обоих переменных!
        int v = value1.getAndAdd(-5);
        value2.addAndGet(v);
        // Между двумя операциями другой поток может вмешаться
    }
    
    // ✅ ИСПОЛЬЗОВАТЬ synchronized для сложных операций
    public synchronized void transferValueCorrect() {
        int v = value1.get() - 5;
        value1.set(v);
        value2.set(value2.get() + v);  // Атомарно для обеих переменных
    }
}

11. Реальный пример: Rate Limiter

import java.util.concurrent.atomic.AtomicLong;

class RateLimiter {
    private AtomicLong requestCount = new AtomicLong(0);
    private AtomicLong lastResetTime = new AtomicLong(System.currentTimeMillis());
    private final long maxRequests = 100;
    private final long resetIntervalMs = 60_000;  // 1 минута
    
    public boolean allowRequest() {
        long now = System.currentTimeMillis();
        long lastReset = lastResetTime.get();
        
        // Сбросить счётчик если прошла минута
        if (now - lastReset > resetIntervalMs) {
            lastResetTime.set(now);
            requestCount.set(0);
        }
        
        // Проверить лимит
        if (requestCount.get() < maxRequests) {
            requestCount.incrementAndGet();
            return true;
        }
        
        return false;
    }
}

Ключевые выводы

Atomic классы:

  1. Что это

    • Потокобезопасные контейнеры для переменных
    • Используют CAS (Compare-and-Swap) вместо блокировок
    • Находятся в пакете java.util.concurrent.atomic
  2. Как работают

    • CAS — быстрая атомарная операция на CPU
    • Lock-free программирование (без мьютексов)
    • Оптимистичный подход (retry если конфликт)
  3. Основные типы

    • AtomicBoolean, AtomicInteger, AtomicLong (примитивы)
    • AtomicReference<T> (объекты)
    • AtomicIntegerArray, AtomicLongArray (массивы)
  4. Когда использовать

    • Простые переменные (счётчики, флаги)
    • Высокопроизводительные системы
    • Низкоконтентные операции
  5. Когда НЕ использовать

    • Сложные блоки кода
    • Несколько переменных, которые нужно обновить атомарно
    • Когда нужна блокировка множества операций

Производительность:

  • Atomic обычно быстрее synchronized в 3-4 раза
  • Лучше для систем с высоким параллелизмом
  • Оптимально для простых операций

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

Что такое Atomic в Java? | PrepBro