AbstractQueued Synchronizer has one synchronization queue and several waiting queues. The types of nodes in the synchronization queue and waiting queue are both static internal class AbstractQueued Synchronizer. Node. The source code analyzed here is based on JDK 8.
ConditionObject includes the following fields
// Head node of waiting queue private transient Node firstWaiter; // End node of waiting queue private transient Node lastWaiter; // When interrupted before being notified, the interrupt mode is set to THROW_IE private static final int REINTERRUPT = 1; // When notified, set interrupt mode to REINTERRUPT private static final int THROW_IE = -1;
The first thing to say is the await() method. If a thread calls the Condition.await() method, the thread will release the lock, construct the node to join the waiting queue and enter the waiting state. When returned from the await() method, the current thread must have acquired the lock associated with Condition. If the await() method is viewed from a queue perspective, when the await() method is called, the first node of the synchronous queue (the node that acquires the lock) moves to the waiting queue of Condition.
public final void await() throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); // Add the current thread to the waiting queue Node node = addConditionWaiter(); // Release lock int savedState = fullyRelease(node); int interruptMode = 0; // The loop determines whether the node has been transferred to the synchronization queue. If the current node is not in the synchronization queue, it indicates that it has just been await. // If no one calls the signal method, the current thread is suspended directly while (!isOnSyncQueue(node)) { LockSupport.park(this); // Here the thread is suspended and stopped running // The ability to execute this indicates that either the signal() method has been invoked or the thread has been interrupted. // So check the reason why the thread is awakened, and if the interrupt is awakened, jump out of the while loop if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) break; } if (acquireQueued(node, savedState) && interruptMode != THROW_IE) interruptMode = REINTERRUPT; // Delete cancelled successor nodes if (node.nextWaiter != null) unlinkCancelledWaiters(); if (interruptMode != 0) reportInterruptAfterWait(interruptMode); }
Source code for the addConditionWaiter() method
private Node addConditionWaiter() { Node t = lastWaiter; // If the tail node's state is not CONDITION, it is removed if (t != null && t.waitStatus != Node.CONDITION) { // Delete canceled nodes and reconnect unlinkCancelledWaiters(); t = lastWaiter; } Node node = new Node(Thread.currentThread(), Node.CONDITION); if (t == null) firstWaiter = node; else t.nextWaiter = node; // Update tail node lastWaiter = node; return node; }
Source code for unlinkCancelledWaiters() method
private void unlinkCancelledWaiters() { // Record every node traversed Node t = firstWaiter; // Save the reference of the precursor node currently traversing the node because the waiting queue is a one-way queue Node trail = null; // Traveling from scratch while (t != null) { // Save references to successor nodes Node next = t.nextWaiter; // Delete if the current traversal node is not CONDITION if (t.waitStatus != Node.CONDITION) { t.nextWaiter = null; if (trail == null) firstWaiter = next; else trail.nextWaiter = next; if (next == null) lastWaiter = trail; } else trail = t; t = next; } }
Here is the source code for the fullyRelease(Node node) method. You can see that the release(int arg) method is called in the fullyRelease(Node node) method, and the tryRelease(int arg) method is called inside the release(int arg) method.
final int fullyRelease(Node node) { boolean failed = true; try { int savedState = getState(); if (release(savedState)) { failed = false; return savedState; } else { throw new IllegalMonitorStateException(); } } finally { if (failed) node.waitStatus = Node.CANCELLED; } } public final boolean release(int arg) { if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false; } protected boolean tryRelease(int arg) { throw new UnsupportedOperationException(); }
Source code for isOnSyncQueue(Node node) method
final boolean isOnSyncQueue(Node node) { if (node.waitStatus == Node.CONDITION || node.prev == null) return false; if (node.next != null) // If has successor, it must be on queue return true; return findNodeFromTail(node); } // Search for whether the node is in the queue from the end of the synchronous queue private boolean findNodeFromTail(Node node) { Node t = tail; for (;;) { if (t == node) return true; if (t == null) return false; t = t.prev; } }
Source code for checkInterruptWhileWaiting(Node node) method
// Return THROW_IE interrupted before notification, REINTERRUPT interrupted after notification, and 0 if not interrupted. private int checkInterruptWhileWaiting(Node node) { return Thread.interrupted() ? (transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) : 0; } final boolean transferAfterCancelledWait(Node node) { if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) { enq(node); return true; } while (!isOnSyncQueue(node)) Thread.yield(); return false; }
Source code of acquireQueued(final Node node, int arg) method
final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { boolean interrupted = false; for (;;) { final Node p = node.predecessor(); if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC failed = false; return interrupted; } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) cancelAcquire(node); } }
private void reportInterruptAfterWait(int interruptMode) throws InterruptedException { if (interruptMode == THROW_IE) throw new InterruptedException(); else if (interruptMode == REINTERRUPT) selfInterrupt(); } static void selfInterrupt() { Thread.currentThread().interrupt(); }