How Java AQS Uses CLH Queues and LockSupport to Manage Thread Blocking
This article explains the internal mechanisms of Java's AbstractQueuedSynchronizer, detailing how the CLH synchronization queue, the shouldParkAfterFailedAcquire method, and the LockSupport utility work together to decide when a thread should spin, block, or be unblocked, with full code examples and step‑by‑step analysis.
When a thread fails to acquire a synchronization state in Java's AbstractQueuedSynchronizer (AQS), it is added to the CLH queue and repeatedly attempts to acquire the state by spinning. During this spin, the thread must determine whether it should block, which is handled by the acquireQueued() method.
Checking the Need to Block
The core check is performed by shouldParkAfterFailedAcquire(Node pred, Node node). The method examines the predecessor node's waitStatus to decide:
If waitStatus == Node.SIGNAL, the current thread should block; the method returns true.
If waitStatus > 0 (CANCELLED), the predecessor is cancelled, so the algorithm walks back to find a valid predecessor and returns false.
Otherwise, the method attempts a CAS to set the predecessor's status to SIGNAL and returns false.
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
return true;
if (ws > 0) {
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}If the method returns true, AQS calls parkAndCheckInterrupt() to block the thread:
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}The park() call suspends the thread until another thread unparks it, while also returning the thread's interrupt status.
Releasing and Unparking Successors
When a thread releases a lock, it must wake up its successor in the queue:
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}The unparkSuccessor(Node node) method finds a suitable successor and invokes LockSupport.unpark():
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next;
if (s == null || s.waitStatus > 0) {
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);
}The algorithm walks backward from the tail because node.next may be null or point to a cancelled node.
LockSupport Basics
Both blocking and unblocking are delegated to LockSupport, which provides low‑level thread parking primitives built on sun.misc.Unsafe:
public static void park() {
UNSAFE.park(false, 0L);
}
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}The native methods behind these calls are:
public native void park(boolean isAbsolute, long time);
public native void unpark(Object obj);In summary, AQS relies on a CLH queue to manage waiting threads, uses shouldParkAfterFailedAcquire to decide whether a thread should spin or block, employs LockSupport.park() to block, and wakes successors via LockSupport.unpark(), all backed by low‑level Unsafe operations.
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.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
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.
