58. Atomic Classes
AtomicInteger, AtomicLong, AtomicReference, and LongAdder provide lock-free atomic operations using hardware CAS (Compare-And-Swap) โ more scalable than synchronized for simple counter/state updates.
Explanation
Compare-And-Swap (CAS): atomically check if a variable still has an expected value; if yes, replace it with a new value; if no (another thread changed it), retry. The CPU provides this as a single instruction โ no lock required.
AtomicInteger / AtomicLong: incrementAndGet(), decrementAndGet(), addAndGet(delta), compareAndSet(expected, update), getAndUpdate(fn). All atomic, all lock-free.
AtomicReference<T>: atomic get/set/CAS on an object reference. Beware the ABA problem: a reference changes from A to B then back to A โ a CAS on A succeeds even though the value changed. Use AtomicStampedReference<T> (integer version stamp) to detect ABA.
LongAdder: designed for high-contention counting. Instead of one shared counter, it maintains a cell per CPU/thread and only sums on sum(). Far lower contention than AtomicLong when many threads increment simultaneously โ but sum() is approximate in the presence of concurrent updates (still correct when no concurrent updates occur at the time of sum()).
Example
import java.util.concurrent.atomic.*;
public class AtomicClasses {
public static void main(String[] args) throws InterruptedException {
// AtomicInteger: lock-free counter
AtomicInteger counter = new AtomicInteger(0);
Thread[] threads = new Thread[100];
for (int i = 0; i < 100; i++) {
threads[i] = new Thread(() -> { for (int j = 0; j < 100; j++) counter.incrementAndGet(); });
threads[i].start();
}
for (Thread t : threads) t.join();
System.out.println("Counter (always 10000): " + counter.get());
// compareAndSet: CAS operation
AtomicInteger val = new AtomicInteger(10);
boolean swapped = val.compareAndSet(10, 20); // expected=10, update=20
System.out.println("CAS succeeded: " + swapped + ", val=" + val.get()); // true, 20
boolean failed = val.compareAndSet(10, 99); // expected=10 but val=20 โ fails
System.out.println("CAS failed: " + !failed + ", val=" + val.get()); // true, 20
// AtomicReference: atomic object reference swap
AtomicReference<String> ref = new AtomicReference<>("initial");
ref.updateAndGet(s -> s.toUpperCase());
System.out.println("Ref: " + ref.get()); // INITIAL
// LongAdder: high-contention counters
LongAdder adder = new LongAdder();
Thread[] adderThreads = new Thread[1000];
for (int i = 0; i < 1000; i++) {
adderThreads[i] = new Thread(() -> adder.increment());
adderThreads[i].start();
}
for (Thread t : adderThreads) t.join();
System.out.println("Adder sum (always 1000): " + adder.sum());
}
}Sources
- https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/atomic/package-summary.html โ Atomic package API
- https://docs.oracle.com/javase/tutorial/essential/concurrency/atomicvars.html โ Atomic variables tutorial