The full picture
- CAS in one line:
CAS(addr, expected, new)returns success iff*addr == expectedand swaps atomically — the 'optimistic update' primitive: read, compute, CAS, retry on failure. That retry loop isAtomicLong.incrementAndGeton x86 (lock xadd/cmpxchg). - The ABA problem: CAS checks the value, not the history — A→B→A slips past. Fixes: version-tagged words (
AtomicStampedReference), or designs where values never recycle (immutable nodes + GC — one quiet reason lock-free structures are easier on the JVM than in C). - Memory barriers: atomics alone don't order other accesses; acquire/release semantics (or full fences) do — this is the hardware floor under the JMM's happens-before and
volatile(a volatile write compiles to a release store, on x86 usually plain store + no reordering thanks to TSO; ARM needs real barriers). - Cost intuition: uncontended CAS ≈ a few ns; contended = cache-line ownership bounces between cores at ~40–100ns per hop — why
LongAdder(striped cells) beatsAtomicLongunder fire, and why per-core/sharded counters are an OS-kernel pattern too.