They provide a way to perform lock-free, thread-safe operations on single variables.
They ensure atomicity — meaning operations like increment, compare, or set happen as one indivisible step — without using synchronized or explicit locks.
In multithreading, simple operations like:
count++;
are not atomic — they compile into multiple steps (read → modify → write).
Two threads can interleave these steps, leading to race conditions.
Example problem:
Thread A reads count = 5
Thread B reads count = 5
Thread A writes count = 6
Thread B writes count = 6 // lost update!
Use synchronized → guarantees atomicity, but has locking overhead.
Atomic classes → use low-level CPU primitives (CAS) to do it lock-free.
It checks if the variable’s value is what you expect, and if so, updates it — atomically.
boolean compareAndSet(expectedValue, newValue)
It’s like saying: "Set to newValue only if the current value is still expectedValue."
If another thread changed it in between, the update fails (returns false), and your thread can retry.
This is powered by hardware instructions via the JVM’s Unsafe class or VarHandle.
Integer / Long / Boolean — Atomic versions of primitive types:
AtomicInteger, AtomicLong, AtomicBoolean
Arrays — Atomic operations on array elements:
AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray
Reference — Atomic updates to object references:
AtomicReference<T>
Field Updaters — Atomic updates to specific fields in existing classes (without making them atomic types):
AtomicIntegerFieldUpdater<T>, etc.
Accumulators — High-performance counters for high-contention scenarios:
LongAdder, LongAccumulator, DoubleAdder, DoubleAccumulator
AtomicInteger counter = new AtomicInteger(0);
counter.incrementAndGet(); // ++i
counter.getAndIncrement(); // i++
counter.addAndGet(5); // += 5
counter.get(); // read current value
counter.compareAndSet(10, 20); // CAS: if ==10 then set to 20
All of these are atomic and thread-safe.