Deep Dive into Java's AbstractQueuedSynchronizer (AQS) and Concurrency Utilities
This article provides an in‑depth analysis of Java's AbstractQueuedSynchronizer (AQS), explaining its internal data structures, lock acquisition and release mechanisms for ReentrantLock and ReentrantReadWriteLock, and demonstrates how higher‑level concurrency utilities such as Condition, CountDownLatch, CyclicBarrier, and Semaphore are built on top of AQS.
Before JDK 1.5 the only synchronization primitive was synchronized , which was inefficient; Doug Lea introduced the high‑performance concurrency framework based on AbstractQueuedSynchronizer (AQS). AQS uses a FIFO double‑linked queue to manage threads waiting for a lock, supporting both exclusive and shared modes.
ReentrantLock implements the Lock interface. Its lock() method first tries a fast CAS to acquire the lock (non‑fair mode) or checks the queue (fair mode). If acquisition fails, the thread is enqueued via addWaiter and may park until it becomes the head of the queue. Unlocking decrements the state and unparks the successor.
ReentrantReadWriteLock splits the 32‑bit state into high 16 bits for read locks and low 16 bits for write locks. Write lock acquisition checks for existing readers or writers, while read lock acquisition ensures no exclusive owner unless it is the current thread. Reentrancy for reads is tracked per‑thread using a ThreadLocal HoldCounter , with a fast path for the first reader.
Condition objects are created via newCondition() . await() adds the current thread to a condition queue, releases the associated lock, and parks the thread. signal() moves the first waiting node from the condition queue to the synchronization queue and unparks it, allowing it to reacquire the lock.
Other JUC components are built on AQS as well:
CountDownLatch initializes its state with a count; await() blocks until the count reaches zero, while countDown() decrements the state and releases waiting threads.
CyclicBarrier uses a ReentrantLock and a Condition to make a set of threads wait until a predefined number have called await() , then optionally runs a barrier action.
Semaphore maintains a permit count in its state; acquire() obtains a permit (shared acquire) and release() returns one, supporting both fair and non‑fair acquisition policies.
The article concludes that mastering AQS and its derived locks and synchronizers enables precise control over concurrency in backend Java applications, and highlights the elegance and performance of Doug Lea's design.
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.