java Condtion await method and signal method analysis

public final void await() throws InterruptedException {
           //The thread has been interrupted and an exception is thrown
	    if (Thread.interrupted())
                throw new InterruptedException();
	   //Join the queue and clean up the threads whose status is not Node.CONDITION in the queue
            Node node = addConditionWaiter ();
            int savedState = fullyRelease(node);
            int interruptMode = 0;
	    / / Determine whether the current node is in the lock queue
            while (!isOnSyncQueue(node)) {
	      //Not in the sync queue, pending.
                LockSupport.park(this);
		// Determine if the thread is interrupted
                if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                    break;
            }
	    // block waiting in the queue
            if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
                interruptMode = REINTERRUPT;
	   //Clear the node that is not the condition value in the queue.
            if (node.nextWaiter != null) // clean up if cancelled
                unlinkCancelledWaiters();
	   //response to interrupt
            if (interruptMode != 0)
                reportInterruptAfterWait(interruptMode);
        }


  /**
   This code is to enqueue the node.
   1. Initialize firstWaiter and lastWaiter when the queue is empty and firstWaiter=lastWaiter.
   2. When the queue is not empty, t.nextWaiter = node; simultaneously update the nextWaiter of firstWaiter and lastWaiter.
    lastWaiter = node; point the tail node to the newly added node.
   3. The key here is that lastWaiter and the last nextWaiter in the queue hold the same reference.
  */
  private Node addConditionWaiter() {
            //point to the last node
            Node t = lastWaiter;
            //Search from the last node and clear the queue if waitStatus is not equal to Node.CONDITION.
            if (t != null && t.waitStatus != Node.CONDITION) {
                unlinkCancelledWaiters();
                t = lastWaiter;
            }
            Node node = new Node(Thread.currentThread(), Node.CONDITION);
            if (t == null)
                firstWaiter = node;
            else
                t.nextWaiter = node;
            lastWaiter = node;
            return node;
        }


 //Clear out the queue if waitStatus is not equal to Node.CONDITION.
 private void unlinkCancelledWaiters() {
            Node t = firstWaiter;
            Node trail = null;
            while (t != null) {
                Node next = t.nextWaiter;
                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;
            }
        }

   // release the lock
    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;
        }
    }

   //Determine whether the node is in the sync queue.
    final boolean isOnSyncQueue(Node node) {
       //Only the waitStatus of the node in the condition will be equal to CONDITION. The node in the condition also has no prev
        if (node.waitStatus == Node.CONDITION || node.prev == null)
            return false;
       //If there is a next node in node, it must be in the sync queue
        if (node.next != null) // If has successor, it must be on queue
            return true;
        /*
         node.prev may not be empty but not yet enqueued because CAS execution may fail. So keep checking.
         */
        return findNodeFromTail(node);
    }
   
   //Start from the end of the queue and traverse to check whether the node is in the sync queue.
    private boolean findNodeFromTail(Node node) {
        Node t = tail;
        for (;;) {
            if (t == node)
                return true;
            if (t == null)
                return false;
            t = t.prev;
        }
    }

     
     private int checkInterruptWhileWaiting(Node node) {
            return Thread.interrupted() ?
	        // thread was interrupted
                (transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
                0;
        }
    
    final boolean transferAfterCancelledWait(Node node) {
       //Set the value of node to 0 and join the sync queue
        if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
	   // enqueue
            enq(node);
            return true;
        }
        /*
         If not spinning in the sync queue.
         */
        while (!isOnSyncQueue(node))
            Thread.yield();
        return false;
    }

     private void reportInterruptAfterWait(int interruptMode)
            throws InterruptedException {
            if (interruptMode == THROW_IE)
                throw new InterruptedException();
            else if (interruptMode == REINTERRUPT)
                selfInterrupt();
        }


 //Look at the signal() method again
 public final void signal() {
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            Node first = firstWaiter;
	    //The first node is removed from the condition queue and added to the sync queue.
            if (first != null)
                doSignal(first);
        }


 private void doSignal(Node first) {
            do {
                if ( (firstWaiter = first.nextWaiter) == null)
                    lastWaiter = null;
                first.nextWaiter = null;
            } while (!transferForSignal(first) &&
                     (first = firstWaiter) != null);
        }


final boolean transferForSignal(Node node) {
        /*
         * Set the state of node to 0.
         */
        if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
            return false;

        /*
         * Splice onto queue and try to set waitStatus of predecessor to
         * indicate that thread is (probably) waiting. If cancelled or
         * attempt to set waitStatus fails, wake up to resync (in which
         * case the waitStatus can be transiently and harmlessly wrong).
         */
	// join the sync queue
        Node p = enq(node);
        int ws = p.waitStatus;
	//If the state of the current thread is cancel, suspend the current thread
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
            LockSupport.unpark(node.thread);
        return true;
    }

    /**Summary: The await method of condition will release the lock and then suspend until it is added to the sync queue. And the signal method is
    Remove firstWaiter from the condition queue and add it to the sync queue.
    Finally wake up the thread in the unlock() method.
    */


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326475935&siteId=291194637