Compare-and-swap and the ABA problem
What compare-and-swap really does, the CAS retry loop with real retry counts under contention, a lock-free Treiber stack, and the ABA problem: how it breaks that stack, why Java's GC makes it rarer, and how stamps fix it.
On this page
- Picture it first: "only if it still says 100"
- Under the hood: one instruction, three letters
- Lock-free vs wait-free, in plain words
- A lock-free stack in a dozen lines
- The ABA problem, told as a story first
- Fixing ABA: compare the history, not only the value
- The approach ladder: a shared stack under many threads
- Real world
- Advanced: contention, spurious failures and VarHandles
- How to say it in the interview
🔁 What is compare-and-swap (CAS)?
CAS is one CPU instruction that says: "set this memory slot to B, but only if it still holds A", and it does the check and the write as one indivisible step. If someone changed the slot in the meantime, nothing is written and you're told you lost. In Java you meet it as AtomicInteger.compareAndSet(expected, newValue), and it's the engine under every atomic class, ConcurrentHashMap and most of java.util.concurrent.
For this lesson, we'll write the CAS retry loop by hand and count how often it loses under real contention, build a lock-free stack on top of it, and then break that stack with the ABA problem: a bug where "it still says A" is true, and everything is wrong anyway. Interviewers love this topic because it starts as "how does AtomicInteger work without a lock?" and ends at "your lock-free stack just handed the same job to two workers. Why?" That second half is where most candidates go quiet, so it's where you can stand out.
What you’ll learn
- Explain compare-and-swap in one sentence and name the hardware behind it (
LOCK CMPXCHGon x86, LL/SC and **LSECAS** on ARM) - Write the CAS retry loop with
AtomicInteger.compareAndSetfrom memory, and predict how many attempts fail under contention - Tell lock-free from wait-free and from blocking, in plain words
- Build a lock-free Treiber stack on an
AtomicReferenceand say exactly when it stays correct - Walk through the ABA problem step by step, and explain why Java's GC prevents most pointer ABA while values, indexes and pooled objects still suffer it
- Fix ABA with
AtomicStampedReference, know whatAtomicMarkableReferenceis really for, and when to reuseConcurrentLinkedDequeinstead - Explain why CAS under heavy contention can be slower than a lock (cache line ping-pong, wasted retries) and what backoff,
getAndAddandLongAdderchange - Use
weakCompareAndSet(spurious failure),compareAndExchangeand **VarHandle** CAS on your own fields
Premium lesson
Keep reading with the Java Pass
The rest of this lesson is part of Java One-Stop Interview Prep: in-depth explanations, diagrams, model answers and how to say them in the interview.
- Picture it first: "only if it still says 100"
- Under the hood: one instruction, three letters
- Lock-free vs wait-free, in plain words
- A lock-free stack in a dozen lines
- The ABA problem, told as a story first
- Fixing ABA: compare the history, not only the value
- The approach ladder: a shared stack under many threads
- Real world
- Advanced: contention, spurious failures and VarHandles
- How to say it in the interview
From ₹299 · one-time payment · No auto-renewal
Further reading (14)
- java.util.concurrent.atomic package summary (Java SE 21): atomic classes, compareAndSet memory effects, AtomicStampedReference and AtomicMarkableReference
- AtomicStampedReference (Java SE 21 API): reference plus int stamp updated atomically; get(int[]) returns both
- AtomicMarkableReference (Java SE 21 API): reference plus boolean mark, used for logically deleted nodes
- AtomicInteger (Java SE 21 API): weakCompareAndSet deprecated since 9 (plain semantics), weakCompareAndSetPlain/Volatile, compareAndExchange
- VarHandle (Java SE 21 API): compareAndSet, weakCompareAndSet with volatile semantics, spurious failure, access modes
- ConcurrentLinkedDeque (Java SE 21 API): lock-free deque since Java 7; size() is not constant-time
- ConcurrentLinkedQueue (Java SE 21 API): based on the Michael and Scott non-blocking queue
- Michael and Scott: Simple, Fast, and Practical Non-Blocking and Blocking Concurrent Queue Algorithms (PODC 1996): counted pointers to avoid ABA when nodes are reused
- JDK-7023898: Intrinsify AtomicLongFieldUpdater.getAndIncrement(): JDK 8 replaced CAS loops in getAndAdd with LOCK XADD intrinsics via Unsafe.getAndAddInt
- Doug Lea: Using JDK 9 Memory Order Modes: VarHandle access modes, weak CAS and spurious failure
- Intel x86 instruction reference: CMPXCHG: compare and exchange; atomic with the LOCK prefix
- Martin Thompson: Single Writer Principle: optimistic CAS still queues under contention; design for one writer
- Wikipedia: Treiber stack and ABA problem: background: Treiber (IBM, 1986), tagged pointers, hazard pointers
- Java Concurrency in Practice (Goetz et al., 2006), chapter 15: atomic variables and nonblocking synchronization, Treiber stack, ABA, CAS vs locks under contention