Java / Java Concurrency and Multi Threading in Java 17 and Java 21 Interview questions
Why does LongAdder outperform AtomicLong under high contention?
AtomicLong funnels every thread's update through compare-and-swap on a single shared 64-bit value. Under light contention that's cheap, but as thread count and update frequency rise, most CAS attempts fail because another thread has already changed the value, forcing repeated retries, and every thread is still fighting over one cache line.
LongAdder adder = new LongAdder(); adder.increment(); // spreads updates across internal cells under contention long total = adder.sum(); // sums cells on demand, only when a total is needed
LongAdder, built on the internal Striped64 mechanism, takes a different approach: under contention, it maintains an array of separate padded Cells, and different threads update different cells (chosen via a per-thread hash, with rehashing on collision), so most updates hit uncontended memory and rarely retry. Reading the total via sum() walks all the cells and adds them together, which is more expensive per read but is a rare operation compared to the very frequent increments.
This trades slower reads for dramatically faster, more scalable writes, which is exactly the right trade-off for use cases like hit counters or request metrics, where increments vastly outnumber reads of the running total. Under low contention with a single or very few threads, AtomicLong is just as fast and uses less memory, so LongAdder's benefit only shows up as concurrent writers scale up.
More Related questions...