Atomic variables and compare-and-swap
Atomic classes use a hardware compare-and-swap instruction to update a value without locking: read, compute, and swap only if nothing changed in between.
count++ is three operations — read, add, write — and two threads can
interleave inside it. AtomicInteger makes the whole update indivisible.
AtomicInteger counter = new AtomicInteger();
counter.incrementAndGet();
// Arbitrary atomic update, retried until it wins the race
counter.updateAndGet(v -> Math.min(v + 1, MAX));
The retry loop is the point: CAS fails when another thread got there first, and
the operation simply tries again with the new value. No thread ever blocks,
which is why atomics outperform locks under moderate contention — and why they
degrade under heavy contention, as retries pile up. LongAdder exists for that
case, spreading updates across cells and summing on read.
Only single variables
CAS covers one variable. Two fields that must change together still need a lock
or an immutable holder object swapped atomically with
AtomicReference.
The ABA problem
A value can change from A to B and back to A between your read and your swap;
CAS sees no difference. AtomicStampedReference attaches a version counter
when that matters.
Volatile is not enough
volatile guarantees visibility and ordering, not atomicity. A volatile int
still loses increments.