Condition is introduced

ConditionObject (Condition) ConditionObject (Condition) ConditionObject (Condition) ConditionObject (Condition) ConditionObject (Condition) ConditionObject (Condition) ConditionObject (Condition) ConditionObject (Condition) ConditionObject (Condition) ConditionObject (Condition) Condition await signal/signalAll is similar to synchronized+wait+notify/notifyAll, and the logic of implementation is similar. When synchronized wait is used, the current thread is placed in a waitSet(a sleeping wait queue that does not compete for a lock), Condition calls await, and puts the current thread on a queue maintained by condition. Notify synchronized moves a random waitSet thread to an enterList. The condition's signal method is used to move the first element in the queue to the Lock's wait queue (AQS's wait queue, which has a chance to compete for the Lock)Copy the code

Code sample

class BoundedContainer{
	
	private String[] elements = new String[10];
	
	private Lock lock = new ReentrantLock();
	
	private Condition notEmptyCondition = lock.newCondition();
	
	private Condition notFullCondition = lock.newCondition();
	
	private int elementCount;
	
	private int putIndex;
	
	private int takeIndex;
	
	
	public void put(String str) {
		
		try {
			lock.lock();
			while(elementCount==elements.length) {
				notFullCondition.await();
			}
			elements[putIndex] = str;
			if(++ putIndex == this.elements.length) {
				putIndex = 0;
			}
			++ elementCount;
			System.out.println("put method: "+Arrays.toString(this.elements));
			
			notEmptyCondition.signal();
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally{ lock.unlock(); }}public String take(a) {
		
		
		try {
			lock.lock();
			while(elementCount==0) {
				notEmptyCondition.await();
			}
			String element = elements[takeIndex];
			elements[takeIndex] = null;
			
			if(++ takeIndex == this.elements.length) {
				takeIndex = 0;
			}
			
			System.out.println("take method: "+Arrays.toString(this.elements));
			
			notFullCondition.signal();
			
			-- elementCount;
			return element;
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
		return null; }}Copy the code

Source code analysis

The member variables included in ConditionObject

// A bidirectional list is maintained internally. This node is AQS node, which is a bidirectional list
/** First node of condition queue. */
private transient Node firstWaiter;
/** Last node of condition queue. */
private transient Node lastWaiter;

Copy the code

Get method newCondition

private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();

//newCondition
public Condition newCondition(a) {
	// Call the synchronizer's newCondition method
    return sync.newCondition();
}

final ConditionObject newCondition(a) {
	/ / new a ConditionObject instance directly and thus the lock each call newCondition method, is to get to the Condition of different instances
    return new ConditionObject();
}

Copy the code

Condition.await()

public final void await(a) throws InterruptedException {
	// Thread interrupts
    if (Thread.interrupted())
        throw new InterruptedException();
    Condition builds a node and places it on the condition's own queue
    Node node = addConditionWaiter();
    Condition (); condition (); condition (); condition ()
    // The current thread wants to release the lock held by it
    int savedState = fullyRelease(node);
    int interruptMode = 0;
    // If the node's waitStatus is node.condition, one of the criteria is whether the node's waitStatus is node.condition
    // Since node is created in CONDITION, we can enter the while block
    while(! isOnSyncQueue(node)) {// LockSupport is a basic utility class, primarily using the unsafe class, which also encapsulates CAS operations
        // the park method is a low-level c++ method that makes the current thread wait and not compete for CPU time slices
         LockSupport.park(this);
        if((interruptMode = checkInterruptWhileWaiting(node)) ! =0)
            break;
    }
    // acquireQueued this method is used in AQS to make the current thread in a for(;). To continuously acquire the lock
    // When a thread is suspended, the lock is contested even when it is woken up. This method is also called in the lock.lock method
    // If you don't fight for the lock, you can wake up with a basket
    if(acquireQueued(node, savedState) && interruptMode ! = THROW_IE) interruptMode = REINTERRUPT;if(node.nextWaiter ! =null) // clean up if cancelled
        unlinkCancelledWaiters();
    if(interruptMode ! =0)
        reportInterruptAfterWait(interruptMode);
}

Copy the code

Condition.signal

public final void signal(a) {
	// Determine if the current thread is the thread that acquired the lock, if not directly throw an exception
    if(! isHeldExclusively())throw new IllegalMonitorStateException();
    // Get the first node of the condition wait queue
    Node first = firstWaiter;
    if(first ! =null)
    	// 
        doSignal(first);
}


private void doSignal(Node first) {
    do {
    	// Set this queue to NULL if there is no next one
        if ( (firstWaiter = first.nextWaiter) == null)
            lastWaiter = null;
        first.nextWaiter = null;
        // Focus on the transferForSignal method
    } while(! transferForSignal(first) && (first = firstWaiter) ! =null);
}

final boolean transferForSignal(Node node) {
    /* * If cannot change waitStatus, the node has been cancelled. */
     // Use CAS to set the node status
    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). */
     // Place the first node in the condition queue on the AQS queue to wait for the lock to be contested
    Node p = enq(node);
    int ws = p.waitStatus;
    // This condition. Signal call is not accessible, but the code exists for a reason
   // The thread that is placed in the AQS wait queue should wake up when another thread releases the lock
   // Set the node's waitStatus to SIGNAL
    if (ws > 0| |! compareAndSetWaitStatus(p, ws, Node.SIGNAL)) LockSupport.unpark(node.thread);return true;
}

Copy the code