How AQS AbstractQueuedSynchronizer Powers Java Concurrency Primitives
AbstractQueuedSynchronizer (AQS) provides a framework that manages a FIFO wait-queue and an integer state variable to enable high-level Java concurrency primitives such as ReentrantLock, CountDownLatch, and Semaphore through exclusive and shared acquisition modes.
The java.util.concurrent (J.U.C) package relies heavily on AQS to implement its synchronization utilities. According to the concurrency notes in the CyC2018/CS-Notes repository, this framework abstracts the mechanics of blocking threads, queuing waiters, and atomic state management so that concrete classes can define only the logic for acquiring and releasing resources.
Core Architecture of AQS AbstractQueuedSynchronizer
The State Variable and CLH Queue
At the heart of AQS AbstractQueuedSynchronizer is a volatile int state field that represents the synchronization status. For a mutex, this might toggle between 0 and 1; for a semaphore, it tracks available permits. AQS organizes waiting threads into a CLH (Craig, Landin, and Hagersten) lock queue, a variant of a FIFO queue where each thread is encapsulated in a Node object.
When a thread fails to acquire the resource, AQS invokes acquire() or acquireShared(), creates a Node for the thread, appends it to the queue tail using CAS operations, and parks the thread using LockSupport.park(). This avoids busy-waiting and delegates thread scheduling to the JVM.
Acquisition and Release Protocols
The framework defines template methods that subclasses must implement:
tryAcquire(int arg): Attempts to acquire the exclusive resource. Returnstrueif successful.tryAcquireShared(int arg): Attempts to acquire the resource in shared mode. Returns a non-negative value if successful.tryRelease(int arg)andtryReleaseShared(int arg): Update the state to release the resource.
When release methods succeed, AQS automatically unparks the successor node in the CLH queue, allowing that thread to retry acquisition. This handshake between release() and acquire() ensures FIFO fairness while maintaining high throughput.
Exclusive vs. Shared Synchronization Modes
Exclusive Mode Implementation
In exclusive mode, only one thread may hold the resource at a time. ReentrantLock uses this pattern by extending AQS through an inner Sync class. When lock() is called, AQS invokes tryAcquire(1), which checks if the state is 0 (unlocked) and performs a CAS to 1. If the state is non-zero and the owner is the current thread, ReentrantLock increments the hold count to support reentrancy.
// ReentrantLock – exclusive mode built on AQS
Lock lock = new ReentrantLock();
Runnable task = () -> {
lock.lock(); // AQS attempts exclusive acquire via tryAcquire
try {
System.out.println(Thread.currentThread().getName() + " holds lock");
Thread.sleep(500);
} catch (InterruptedException ignored) {}
finally {
lock.unlock(); // AQS releases state and unparks next waiter
}
};
ExecutorService exec = Executors.newFixedThreadPool(3);
for (int i = 0; i < 3; i++) exec.execute(task);
exec.shutdown();
Shared Mode Implementation
Shared mode allows multiple threads to access the resource concurrently. Implementations like CountDownLatch and Semaphore override tryAcquireShared(int arg). In CountDownLatch, the initial state represents the count; tryAcquireShared returns 1 only when the state reaches 0, allowing all waiting threads to proceed simultaneously.
// CountDownLatch – uses AQS in shared mode
CountDownLatch latch = new CountDownLatch(3);
ExecutorService exec = Executors.newFixedThreadPool(3);
for (int i = 0; i < 3; i++) {
exec.execute(() -> {
System.out.println(Thread.currentThread().getName() + " doing work");
latch.countDown(); // releases a permit in AQS (decrements state)
});
}
latch.await(); // acquires shared permit (blocks until count == 0)
System.out.println("All work done");
exec.shutdown();
Semaphore uses shared mode to decrement permit counts. When acquire() is called, tryAcquireShared attempts to subtract 1 from the state; if the result is non-negative, the acquisition succeeds.
// Semaphore – AQS manages a permit counter
Semaphore sem = new Semaphore(2); // at most 2 threads may proceed
ExecutorService exec = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
exec.execute(() -> {
try {
sem.acquire(); // AQS tries to decrement state; may block if 0
System.out.println(Thread.currentThread().getName() + " acquired");
Thread.sleep(1000); // simulate work
} catch (InterruptedException e) { }
finally {
sem.release(); // AQS increments state and unparks waiting thread
}
});
}
exec.shutdown();
How J.U.C Primitives Use AQS AbstractQueuedSynchronizer
The java.util.concurrent primitives delegate synchronization logic to AQS through specialized inner classes. The following table maps each primitive to its AQS implementation details as documented in notes/Java 并发.md:
| Primitive | AQS Subclass | Synchronization Mode | State Management |
|---|---|---|---|
ReentrantLock |
AbstractQueuedSynchronizer (inner Sync) |
Exclusive | 0/1 state with reentrant hold count |
CountDownLatch |
AbstractQueuedSynchronizer (inner Sync) |
Shared | tryAcquireShared returns positive only when state == 0 |
Semaphore |
AbstractQueuedSynchronizer (inner Sync) |
Shared | tryAcquireShared decrements available permits |
CyclicBarrier |
AbstractQueuedSynchronizer (inner Sync) |
Shared | tryAcquireShared returns 0 when all parties arrive |
Each primitive overrides the template methods to define resource-specific logic while inheriting the robust queue management, cancellation handling, and interrupt response from AQS AbstractQueuedSynchronizer.
Summary
- AQS AbstractQueuedSynchronizer provides the foundational framework for
java.util.concurrentlocks and synchronizers by managing a CLH FIFO queue and an integer state variable. - Subclasses implement
tryAcquire/tryReleasefor exclusive resources ortryAcquireShared/tryReleaseSharedfor shared resources, leaving queue mechanics to the framework. - Exclusive mode powers
ReentrantLock, ensuring only one thread holds the lock at a time. - Shared mode enables
CountDownLatch,Semaphore, andCyclicBarrierto allow multiple concurrent accessors. - The framework uses parking/unparking via
LockSupportto block and wake threads efficiently without spin-waiting.
Frequently Asked Questions
What is the primary purpose of AQS AbstractQueuedSynchronizer?
AQS AbstractQueuedSynchronizer serves as a reusable synchronization framework that abstracts the complexity of blocking threads, managing wait queues, and performing atomic state updates. It allows developers to build custom locks and synchronizers by implementing only the resource-specific acquisition logic while inheriting robust queue management.
How does AQS AbstractQueuedSynchronizer handle thread blocking?
When a thread fails to acquire the resource, AQS encapsulates it in a Node and appends it to the CLH queue tail using CAS operations. The thread is then parked using LockSupport.park(), which removes it from the CPU scheduler until another thread calls release() and unparks the queue head via LockSupport.unpark().
What distinguishes exclusive mode from shared mode in AQS?
Exclusive mode, used by ReentrantLock, allows only one thread to hold the resource, and subsequent acquirers are queued. Shared mode, used by CountDownLatch and Semaphore, permits multiple threads to acquire the resource simultaneously; tryAcquireShared returns a non-negative value to signal success, and AQS propagates the release signal to subsequent nodes if the resource remains available.
Which Java classes extend AQS AbstractQueuedSynchronizer?
Core J.U.C classes including ReentrantLock (via inner class Sync), ReentrantReadWriteLock, CountDownLatch, Semaphore, and CyclicBarrier all extend AQS AbstractQueuedSynchronizer. Each implements the protected template methods to define custom synchronization policies while reusing the framework's queue infrastructure.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →