Understanding CAS, ABA Problem, and AQS in Java Concurrency: Fair vs Non‑Fair Locks
This article explains the fundamentals of CAS (compare‑and‑swap), the ABA problem and its version‑stamp solution, introduces AbstractQueuedSynchronizer (AQS) as the core of Java concurrency utilities, and compares fair and non‑fair lock implementations in ReentrantLock with code examples and diagrams.
The author, a senior architect, introduces core Java concurrency concepts, starting with CAS (compare‑and‑swap) and its three values (new, expected, current) and shows how AtomicInteger uses CAS.
What is CAS
CAS stands for compare‑and‑swap, a low‑level atomic operation that updates a variable only if its current value matches an expected value.
Example code using AtomicInteger demonstrates a successful CAS operation:
// similar to a global shared variable V
AtomicInteger atomicInteger = new AtomicInteger(0);
// CAS operation
boolean b = atomicInteger.compareAndSet(0, 1);
System.out.println("whether modified successfully:" + b);
System.out.println("modified value:" + atomicInteger.get());Output: whether modified successfully:true modified value:1
ABA Problem
The ABA problem occurs when a variable changes from value A to B and back to A; a thread checking only the value sees no change and may proceed incorrectly. Adding a version stamp (or timestamp) to each update solves the issue by making each change unique.
What is AQS
AQS (AbstractQueuedSynchronizer) is the foundation of many Java concurrency utilities such as ReentrantLock and CountDownLatch . It manages a FIFO wait queue of threads and provides exclusive or shared acquisition semantics.
Typical usage with ReentrantLock :
ReentrantLock reentrantLock = new ReentrantLock();
reentrantLock.lock();The lock method delegates to an internal Sync subclass that extends AbstractQueuedSynchronizer :
public void lock() {
sync.lock();
}Two concrete Sync subclasses implement the lock: FairSync (fair lock) and NonfairSync (default, non‑fair lock).
Fair Lock vs Non‑Fair Lock
Fair lock : threads acquire the lock in strict FIFO order, preventing starvation but incurring higher latency and lower throughput due to frequent kernel‑mode thread wake‑ups.
Non‑fair lock : threads compete for the lock each time it becomes available; the winning thread may “cut in line”, yielding higher throughput but risking starvation for some threads.
Diagram of a fair lock shows threads T1‑T4 queued; when T1 releases, only T2 is unparked and given the lock.
In a non‑fair lock, any thread may acquire the lock immediately after it is released, reducing context‑switch overhead.
The design choice favors non‑fair locks for most Java libraries because they avoid kernel‑mode transitions when a thread can acquire the lock directly after the previous holder releases it.
Implementation Details
AQS uses a doubly‑linked queue (prev/next pointers) to simplify node removal when a thread is interrupted or cancelled, improving performance over a singly‑linked CLH queue.
Low‑level CAS instruction used by AQS is the lock cmpxchg assembly operation:
lock cmpxchgOverall, the article provides a concise yet thorough overview of how Java implements low‑level synchronization primitives, the trade‑offs between fairness and performance, and practical code snippets for developers.
Top Architect
Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.