Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
AtomicInteger: операции и практическое применение
AtomicInteger — это класс в java.util.concurrent.atomic, предоставляющий потокобезопасные атомарные операции над целыми числами. Это критичный инструмент для многопоточного программирования без явных блокировок (lock-free).
Что такое AtomicInteger?
AtomicInteger использует Compare-and-Swap (CAS) операции для достижения потокобезопасности без synchronized блоков:
// Обычный int — НЕ потокобезопасен
public class Counter {
private int count = 0; // ❌ Проблема: race condition
public void increment() {
count++; // Не атомарная операция!
// Thread 1 читает count, Thread 2 читает count,
// оба видят 0, оба пишут 1. Вместо +2, только +1
}
}
// AtomicInteger — потокобезопасен
public class AtomicCounter {
private AtomicInteger count = new AtomicInteger(0); // ✅ Потокобезопасно
public void increment() {
count.incrementAndGet(); // Гарантировано атомарно
}
}
Основные операции AtomicInteger
1. Операции чтения и записи
AtomicInteger counter = new AtomicInteger(10);
// Получить текущее значение
int value = counter.get(); // 10
// Установить значение
counter.set(20); // counter = 20
// Получить старое значение и установить новое
int oldValue = counter.getAndSet(30); // oldValue = 20, counter = 30
2. Арифметические операции
AtomicInteger counter = new AtomicInteger(10);
// Увеличить на 1 и получить новое значение
int newValue = counter.incrementAndGet(); // newValue = 11, counter = 11
// Получить текущее и увеличить на 1
int oldValue = counter.getAndIncrement(); // oldValue = 11, counter = 12
// Уменьшить на 1
counter.decrementAndGet(); // counter = 11
counter.getAndDecrement(); // counter = 10
// Добавить значение
counter.addAndGet(5); // counter = 15
counter.getAndAdd(3); // oldValue = 15, counter = 18
3. Compare-and-Swap (CAS) операция
Это ключевая операция, лежащая в основе AtomicInteger:
AtomicInteger counter = new AtomicInteger(10);
// compareAndSet: если текущее значение == ожидаемое, установить новое
boolean updated = counter.compareAndSet(10, 20); // true, counter = 20
updated = counter.compareAndSet(10, 30); // false, counter остаётся 20
// Практический пример: безопасная инкрементация
public class SafeCounter {
private AtomicInteger count = new AtomicInteger(0);
public void safeIncrement() {
int oldValue;
int newValue;
do {
oldValue = count.get();
newValue = oldValue + 1;
} while (!count.compareAndSet(oldValue, newValue));
// Если другой thread изменил count между get и set, повторить
}
}
4. Атомарные обновления с функцией
AtomicInteger counter = new AtomicInteger(10);
// updateAndGet: применить функцию и получить результат
int newValue = counter.updateAndGet(x -> x * 2); // counter = 20
// getAndUpdate: получить старое значение, применить функцию
int oldValue = counter.getAndUpdate(x -> x + 5); // oldValue = 20, counter = 25
// Практический пример: условное обновление
public class LimitedCounter {
private AtomicInteger counter = new AtomicInteger(0);
private static final int MAX = 100;
public boolean increment() {
return counter.updateAndGet(current ->
current < MAX ? current + 1 : current
) != counter.get();
}
}
Практические примеры использования
1. Счётчик запросов (без блокировок)
public class RequestCounter {
private AtomicInteger successCount = new AtomicInteger(0);
private AtomicInteger failureCount = new AtomicInteger(0);
public void recordSuccess() {
successCount.incrementAndGet();
}
public void recordFailure() {
failureCount.incrementAndGet();
}
public void printStats() {
System.out.println("Success: " + successCount.get());
System.out.println("Failure: " + failureCount.get());
}
}
// Использование в многопоточной среде
ExecutorService executor = Executors.newFixedThreadPool(10);
RequestCounter counter = new RequestCounter();
for (int i = 0; i < 1000; i++) {
executor.submit(() -> {
try {
// Имитация запроса
counter.recordSuccess();
} catch (Exception e) {
counter.recordFailure();
}
});
}
2. Простой ID генератор
public class IdGenerator {
private AtomicInteger nextId = new AtomicInteger(1);
public int generateId() {
return nextId.getAndIncrement();
}
public void reset() {
nextId.set(1);
}
}
// Использование
IdGenerator generator = new IdGenerator();
int id1 = generator.generateId(); // 1
int id2 = generator.generateId(); // 2
int id3 = generator.generateId(); // 3
3. Флаг для управления потоками
public class Worker {
private AtomicInteger isRunning = new AtomicInteger(0); // 0 = stopped, 1 = running
public void start() {
if (isRunning.compareAndSet(0, 1)) {
System.out.println("Worker started");
// Запустить рабочий процесс
} else {
System.out.println("Already running");
}
}
public void stop() {
if (isRunning.compareAndSet(1, 0)) {
System.out.println("Worker stopped");
}
}
public boolean isRunning() {
return isRunning.get() == 1;
}
}
4. Буферизованный счётчик (batch операции)
public class BatchCounter {
private AtomicInteger counter = new AtomicInteger(0);
private static final int BATCH_SIZE = 1000;
public void addWithBatch(int amount) {
int current = counter.addAndGet(amount);
if (current >= BATCH_SIZE) {
// Отправить batch
flush();
counter.set(current % BATCH_SIZE);
}
}
private void flush() {
System.out.println("Flushing batch...");
}
}
Производительность: AtomicInteger vs synchronized
public class PerformanceComparison {
// ❌ С synchronized
static class SynchronizedCounter {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized int get() {
return count;
}
}
// ✅ С AtomicInteger (намного быстрее)
static class AtomicCounter {
private AtomicInteger count = new AtomicInteger(0);
public void increment() {
count.incrementAndGet();
}
public int get() {
return count.get();
}
}
// Бенчмарк (упрощённо):
// SynchronizedCounter: ~1000 ns per operation
// AtomicInteger: ~100 ns per operation (в 10 раз быстрее!)
}
Когда использовать AtomicInteger?
✅ Используй, когда:
- Нужен простой счётчик или флаг
- Хочешь избежать блокировок
- Нужна высокая производительность
- Операция — простая арифметика
❌ Не используй, когда:
- Нужны сложные операции (используй synchronized)
- Нужна транзакционность (используй базу данных)
- Нужны коллекции (используй ConcurrentHashMap, etc.)
AtomicInteger vs другие Atomic классы
// Для других типов данных
AtomicLong counter = new AtomicLong(0L);
AtomicBoolean flag = new AtomicBoolean(false);
AtomicReference<String> ref = new AtomicReference<>("initial");
AtomicIntegerArray array = new AtomicIntegerArray(10);
// Массивы для параллельной работы
AtomicIntegerArray counters = new AtomicIntegerArray(100);
counters.incrementAndGet(0); // Потокобезопасно для каждого индекса
Вывод
AtomicInteger — это:
- Потокобезопасный счётчик без явных блокировок
- Основан на CAS операциях
- Намного быстрее synchronized
- Идеален для простых счётчиков и флагов
- Встроен в java.util.concurrent и всегда доступен
Владение AtomicInteger показывает понимание concurrency в Java и способность писать высокопроизводительный многопоточный код.