← Назад к вопросам
Что такое 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 классы:
-
Что это
- Потокобезопасные контейнеры для переменных
- Используют CAS (Compare-and-Swap) вместо блокировок
- Находятся в пакете
java.util.concurrent.atomic
-
Как работают
- CAS — быстрая атомарная операция на CPU
- Lock-free программирование (без мьютексов)
- Оптимистичный подход (retry если конфликт)
-
Основные типы
- AtomicBoolean, AtomicInteger, AtomicLong (примитивы)
- AtomicReference<T> (объекты)
- AtomicIntegerArray, AtomicLongArray (массивы)
-
Когда использовать
- Простые переменные (счётчики, флаги)
- Высокопроизводительные системы
- Низкоконтентные операции
-
Когда НЕ использовать
- Сложные блоки кода
- Несколько переменных, которые нужно обновить атомарно
- Когда нужна блокировка множества операций
Производительность:
- Atomic обычно быстрее synchronized в 3-4 раза
- Лучше для систем с высоким параллелизмом
- Оптимально для простых операций
Atomic классы — это мощный инструмент для написания эффективного многопоточного кода в Java!