Deep Dive into Java's AbstractQueuedSynchronizer (AQS) and ReentrantLock
This article provides a comprehensive analysis of Java's AbstractQueuedSynchronizer (AQS) framework, explains how ReentrantLock is built on AQS, walks through lock acquisition and release mechanisms, discusses queue management, and demonstrates how to create a custom lock using AQS.
Java's concurrency utilities rely heavily on the AbstractQueuedSynchronizer (AQS) framework, which offers a low‑level FIFO queue and atomic state management to implement various synchronizers such as ReentrantLock, ReentrantReadWriteLock, Semaphore, and CountDownLatch.
ReentrantLock Overview
ReentrantLock is a re‑entrant exclusive lock whose implementation is based on AQS. It supports both fair and non‑fair acquisition strategies, and its core methods— lock(), unlock(), tryLock() —delegate to AQS's acquire and release methods.
// Simplified ReentrantLock usage
ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
// critical section
} finally {
lock.unlock();
}AQS Core Concepts
AQS maintains a volatile int state representing the synchronization state and a doubly‑linked CLH‑style queue of Node objects. Each node holds a thread reference, a wait status, and links to predecessor and successor nodes.
private volatile int state; // synchronization state
static final class Node {
volatile int waitStatus;
volatile Node prev;
volatile Node next;
Thread thread;
// ... other fields ...
}Lock Acquisition Process
When a thread calls lock(), AQS executes acquire(1). If tryAcquire(1) fails, the thread is added to the wait queue via addWaiter(Node.EXCLUSIVE) and then repeatedly attempts to acquire the lock in acquireQueued. The algorithm checks whether the node is at the head of the queue; if so, it retries tryAcquire. Otherwise, it may park the thread using LockSupport.park() until it is unparked by a predecessor.
public final void acquire(int arg) {
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}Queue Management and Cancellation
Nodes can enter a CANCELLED state when a thread gives up waiting or is interrupted. The cancelAcquire method removes cancelled nodes from the queue, updates predecessor and successor links, and may unpark the next waiting thread.
private void cancelAcquire(Node node) {
if (node == null) return;
node.thread = null;
// Skip cancelled predecessors
Node pred = node.prev;
while (pred.waitStatus > 0)
node.prev = pred = pred.prev;
// Adjust links and possibly unpark successor
// ...
}Lock Release Process
Calling unlock() invokes release(1), which delegates to the synchronizer's tryRelease. If the lock becomes fully released (state reaches zero), AQS unparks the successor of the head node.
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}Applications in the JUC Package
AQS underpins many Java concurrency utilities: ReentrantLock (exclusive lock), ReentrantReadWriteLock (shared/exclusive bits), Semaphore (permits count), CountDownLatch (countdown), and ThreadPoolExecutor (worker state). Each tool stores its specific state in the AQS state field and implements the required tryAcquire / tryRelease methods.
Creating a Custom Lock with AQS
The article demonstrates a minimal custom lock named LeeLock that extends AQS, providing simple exclusive acquisition and release logic.
public class LeeLock {
private static final class Sync extends AbstractQueuedSynchronizer {
@Override protected boolean tryAcquire(int arg) {
return compareAndSetState(0, 1);
}
@Override protected boolean tryRelease(int arg) {
setState(0);
return true;
}
@Override protected boolean isHeldExclusively() {
return getState() == 1;
}
}
private final Sync sync = new Sync();
public void lock() { sync.acquire(1); }
public void unlock() { sync.release(1); }
}A test program runs two threads that each increment a shared counter 10,000 times under LeeLock, consistently producing the expected total of 20,000, illustrating correct mutual exclusion.
Conclusion
The article emphasizes that understanding AQS is essential for mastering Java's high‑performance concurrency utilities and for building custom synchronizers when needed.
References
Lea D. The java.util.concurrent synchronizer framework. Science of Computer Programming, 2005.
Java Concurrency in Practice.
不可不说的Java“锁”事 (WeChat article).
Author
Li Zhuo, R&D Engineer at Meituan's In‑Store Accommodation and Ticket Business team.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Qunar Tech Salon
Qunar Tech Salon is a learning and exchange platform for Qunar engineers and industry peers. We share cutting-edge technology trends and topics, providing a free platform for mid-to-senior technical professionals to exchange and learn.
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.
