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

Для чего нужны Atomic типы?

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

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

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

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

# Atomic типы в Java

Atomic типы — это набор потокобезопасных классов из пакета java.util.concurrent.atomic, предоставляющих атомарные операции над примитивными типами данных и объектами без явной синхронизации.

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

Atomic типы нужны для:

  1. Потокобезопасности без locks — операции выполняются атомарно без блокировок
  2. Высокой производительности — CAS (Compare-And-Swap) операции быстрее чем synchronized
  3. Простоты кода — более читаемо, чем synchronized блоки
  4. Избежания deadlocks — нет явных блокировок, поэтому нет возможности deadlock

Семейство Atomic типов

1. AtomicInteger

Атомарная переменная целого типа int:

AtomicInteger counter = new AtomicInteger(0);

// Базовые операции
counter.get();                // Получить значение
counter.set(5);               // Установить значение

// Атомарные операции
counter.incrementAndGet();    // ++counter, вернуть новое
counter.decrementAndGet();    // --counter, вернуть новое
counter.getAndIncrement();    // counter++, вернуть старое
counter.getAndDecrement();    // counter--, вернуть старое

// Операции с другими значениями
counter.addAndGet(10);        // counter += 10, вернуть новое
counter.getAndAdd(10);        // counter += 10, вернуть старое

// Compare-And-Swap
counter.compareAndSet(5, 20); // Установить 20 если текущее == 5

// Обновить если меньше/больше
counter.accumulateAndGet(5, Integer::max);  // max(5, current)
counter.accumulateAndGet(5, Integer::min);  // min(5, current)

2. AtomicLong

Атомарная переменная длинного целого типа long:

AtomicLong timestamp = new AtomicLong(System.currentTimeMillis());

timestamp.incrementAndGet();
timestamp.addAndGet(1000);

// Полезна для счетчиков в масштабе (большие числа)
AtomicLong totalRequests = new AtomicLong(0);
totalRequests.incrementAndGet();

3. AtomicBoolean

Атомарная переменная логического типа boolean:

AtomicBoolean isRunning = new AtomicBoolean(true);

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

// CAS для флагов
boolean oldValue = isRunning.getAndSet(true);

// Использование в условиях
if (!isRunning.compareAndSet(true, false)) {
    System.out.println("Уже остановлено");
}

4. AtomicReference<T>

Атомарная ссылка на объект любого типа:

class User {
    String name;
    int age;
}

AtomicReference<User> userRef = new AtomicReference<>(new User());

// Получить и установить
User user = userRef.get();
userRef.set(new User());

// CAS для объектов
User expectedUser = userRef.get();
UserRef.compareAndSet(expectedUser, new User());

// Обновить объект атомарно
AtomicReference<StringBuilder> sb = 
    new AtomicReference<>(new StringBuilder("Hello"));

sb.updateAndGet(s -> {
    s.append(" World");
    return s;
});

5. AtomicIntegerArray

Массив атомарных int значений:

AtomicIntegerArray array = new AtomicIntegerArray(10);

// Операции с элементами массива
array.set(0, 5);
int value = array.get(0);

array.incrementAndGet(0);
array.addAndGet(1, 10);

// CAS для элемента
array.compareAndSet(0, 5, 20);

6. AtomicLongArray

Массив атомарных long значений:

AtomicLongArray array = new AtomicLongArray(10);
array.set(0, 1000000L);
array.addAndGet(0, 500000L);

7. AtomicReferenceArray<E>

Массив атомарных ссылок на объекты:

AtomicReferenceArray<String> array = new AtomicReferenceArray<>(5);
array.set(0, "Hello");
String value = array.get(0);
array.compareAndSet(0, "Hello", "World");

Практические примеры

Пример 1: Счетчик веб-запросов

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

Пример 2: Управление потоком выполнения

public class WorkerThread extends Thread {
    private AtomicBoolean shouldStop = new AtomicBoolean(false);
    private AtomicLong processedItems = new AtomicLong(0);
    
    @Override
    public void run() {
        while (!shouldStop.get()) {
            // Обработать элемент
            processItem();
            processedItems.incrementAndGet();
        }
    }
    
    public void stop() {
        shouldStop.set(true);
    }
    
    public long getProcessedCount() {
        return processedItems.get();
    }
    
    private void processItem() {
        // Логика обработки
    }
}

Пример 3: Безопасное кэширование объектов

public class CachedConnection {
    private AtomicReference<DatabaseConnection> connection =
        new AtomicReference<>();
    
    public DatabaseConnection getConnection() {
        DatabaseConnection conn = connection.get();
        if (conn == null) {
            DatabaseConnection newConn = new DatabaseConnection();
            if (connection.compareAndSet(null, newConn)) {
                return newConn;
            } else {
                // Другой поток создал соединение первым
                return connection.get();
            }
        }
        return conn;
    }
}

Пример 4: Счетчик активных соединений

public class ConnectionPool {
    private AtomicInteger activeConnections = new AtomicInteger(0);
    private static final int MAX_CONNECTIONS = 100;
    
    public boolean acquireConnection() {
        int current = activeConnections.get();
        if (current >= MAX_CONNECTIONS) {
            return false;
        }
        return activeConnections.compareAndSet(current, current + 1);
    }
    
    public void releaseConnection() {
        activeConnections.decrementAndGet();
    }
    
    public int getActiveConnections() {
        return activeConnections.get();
    }
}

Пример 5: Версионирование данных

public class VersionedData {
    private AtomicReference<DataVersion> current =
        new AtomicReference<>(new DataVersion(1, "initial"));
    
    public DataVersion update(String newData) {
        DataVersion oldVersion = current.get();
        DataVersion newVersion = new DataVersion(
            oldVersion.version + 1,
            newData
        );
        
        if (current.compareAndSet(oldVersion, newVersion)) {
            return newVersion;
        } else {
            // Другой поток обновил данные
            return update(newData);
        }
    }
}

Compare-And-Swap (CAS) механизм

AtomicInteger counter = new AtomicInteger(5);

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

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

// Правильный способ: loop until success
int expectedValue;
int newValue;
do {
    expectedValue = counter.get();
    newValue = expectedValue * 2;
} while (!counter.compareAndSet(expectedValue, newValue));

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

// Синхронизированный счетчик (медленнее)
public class SyncCounter {
    private int count = 0;
    public synchronized void increment() { count++; }
    public synchronized int get() { return count; }
}

// Atomic счетчик (быстрее)
public class AtomicCounter {
    private AtomicInteger count = new AtomicInteger(0);
    public void increment() { count.incrementAndGet(); }
    public int get() { return count.get(); }
}

// Бенчмарк
public class PerformanceTest {
    public static void main(String[] args) {
        // AtomicCounter примерно в 2-3 раза быстрее
        // в многопоточной среде благодаря CAS
    }
}

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

Используй Atomic для:

  • Простых счетчиков
  • Флагов управления потоками
  • Кэширования одного объекта
  • Low-contention сценариев (мало конкуренции)
  • Высоконагруженных систем

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

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

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

  • Работают только с примитивами и одиночными объектами
  • Не подходят для синхронизации нескольких переменных
  • Могут быть неэффективны при очень высокой конкуренции
  • Требуют понимания CAS операций

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

Для чего нужны Atomic типы? | PrepBro