What are atomic classes (AtomicInteger, etc.), and how do they achieve thread safety without locks?
Quick Answer
Classes like AtomicInteger, AtomicLong, and AtomicReference in java.util.concurrent.atomic provide lock-free, thread-safe operations (get, set, incrementAndGet, compareAndSet) for single variables, implemented using CAS (compare-and-swap) CPU instructions rather than synchronized blocks. This avoids the overhead of lock acquisition/contention for simple counters and flags, at the cost of only covering a single variable's atomicity rather than an arbitrary critical section.
Detailed Answer
java.util.concurrent.atomic provides wrapper classes (AtomicInteger, AtomicLong, AtomicBoolean, AtomicReference<V>, ...) offering thread-safe, lock-free operations on a single variable:
AtomicInteger counter = new AtomicInteger(0);
counter.incrementAndGet(); // atomic ++counter, no synchronized needed
counter.compareAndSet(5, 10); // atomically: if current value == 5, set to 10
counter.getAndAdd(3); // atomic counter += 3, returns old value
How it works: instead of a lock, these classes use the CPU's native CAS (compare-and-swap) instruction, exposed via sun.misc.Unsafe/java.lang.invoke.VarHandle. CAS atomically checks "is the current value still what I last read?" and, if so, swaps in the new value — all as one indivisible hardware operation. If another thread changed the value in between, the CAS fails and the atomic class's method retries the read-modify-write loop internally until it succeeds.
// conceptually what incrementAndGet() does:
do {
int current = get();
int next = current + 1;
} while (!compareAndSet(current, next));
Why this beats a plain synchronized int counter for simple cases: no thread ever blocks waiting for a lock — under contention, a losing thread just retries its CAS loop rather than parking and waking up (which involves OS scheduling overhead). For low-to-moderate contention, this is significantly faster than lock-based synchronization.
Limitation: atomics only cover a single variable's atomicity. If you need multiple related variables updated together consistently (e.g., a bank account's balance and its transaction count), you still need a lock (or a single AtomicReference to an immutable object holding both fields together).