Atomic - принцип работы
Atomic
классы в Java, такие как AtomicInteger
, AtomicLong
, AtomicReference
и т.д., предоставляют атомарные операции чтения и записи для соответствующих типов данных. Они позволяют избежать гонок данных (data races) путем гарантированного выполнения операций в единой атомарной операции. Вот основные особенности и принципы работы под капотом Atomic
классов:
Атомарные операции:
Atomic
классы предоставляют методы для атомарных операций чтения и записи, таких как getAndIncrement()
, compareAndSet()
, getAndSet()
и другие.Использование Compare-and-Swap (CAS):
Atomic
классах, является алгоритм Compare-and-Swap.Примитивы памяти:
Atomic
классы также используют примитивы памяти, такие как volatile
, чтобы гарантировать видимость изменений переменных между потоками.volatile
обеспечивает, что операции чтения и записи к переменной будут видны всем потокам независимо от кеша потока.Lock-Free алгоритмы:
Atomic
классах реализованы с использованием lock-free алгоритмов.Оптимизация памяти и производительности:
Atomic
классы обычно оптимизированы для минимального использования памяти и максимальной производительности.AtomicInteger
в некоторых реализациях используется специальный характеристический бит, который позволяет уменьшить количество CAS операций в определенных сценариях использования.Платформозависимая реализация:
Atomic
классов может отличаться в зависимости от платформы и JVM.import java.util.concurrent.atomic.AtomicInteger;
public class AtomicExample {
public static void main(String[] args) {
AtomicInteger counter = new AtomicInteger(0);
// Поток инкрементирует счётчик 1000 раз
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.incrementAndGet(); // Атомарная операция инкремента
}
});
// Поток декрементирует счётчик 1000 раз
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.decrementAndGet(); // Атомарная операция декремента
}
});
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Counter value: " + counter.get());
}
}
В этом примере AtomicInteger
используется для подсчёта суммы инкрементов и декрементов из двух параллельных потоков. Методы incrementAndGet()
и decrementAndGet()
предоставляют атомарные операции инкремента и декремента, соответственно. Это означает, что каждая операция инкремента или декремента будет выполнена целиком до того, как другой поток сможет выполнить свою операцию, и гонки данных не произойдет.