Source code analysis-Condition combined with AQS analysis

Condition  

 

 

 As shown in the figure, under the java.util.concurrent.locks package, it is at the same level as AQS

The main method is await(): the thread currently holding the lock enters the wait

       signal(): wake up waiting threads to re-queue to grab the lock

      

Condition is often used in producer-consumer scenarios, responsible for the blocking use of producer-consumers

 

Different from Object's wait() notify() is based on the monitor lock of the object, and Condition is based on the thread that holds the lock. The lock must be held to operate await() and signal()

 

Conventional usage

 

Production stay blocked

 

 

Consumer wakes up producer

 

 

 

Deep into the source code~

 

Add the important attributes of the Node node

 

 

 waitStatus represents the status of the successor node of the current node, CONDITION, CANCELLED, PROPAGETE, SIGNAL are several states respectively

The value of CONDITION is -2, which means the current node has joined the condition queue

CANCELLED value of 1 means that the current node thread cancels the lock grab (will be cleared out in the future)

SIGNAL value -1 means that the subsequent node thread needs to wake up

 

Start with ReentrantLock's newCondition()

 

 

 

 

 

 

 

 AQS inner class ConditionObject

Important attributes:

firstWaiter. The head of the conditional queue

lastWaiter the end of the conditional queue

 

Condition maintains a conditional queue, which is different from the blocking queue of AQS. Node is responsible for competing for locks. The conditional queue stores the Node that is blocked and released the lock.

 

 

 

 The await() method, the node is enqueued to the conditional queue, the signal() method is dequeued from the head of the team, enqueued to the blocking queue, queued to grab the lock, and the await() method before the lock can be returned before it can continue.

Source code analysis starts from await()

1. AQS's await () no-argument method implementation realizes that the thread currently holding the lock is blocked, cannot continue, and releases the lock

 

 

 

 if (Judging whether the thread is interrupted at the beginning) If it has been interrupted, it is thrown directly

Create a Node and add it to the condition queue of Condition; //see 2 for details

Release the lock and retain the state value before releasing the lock; (in order to restore it after the signal) //see 3 for details

while (the current thread is not transferred to the blocking queue and simply does not go deep) {

  Suspend the current thread;

  if (whether someone interrupted the suspension of the thread //see 5 for details)  

    If it is interrupted and transferred to the blocking queue, exit the loop.

}

if (interrupt the current node again, this time interrupted in the blocking queue && before the interrupt flag is not throwing an exception, indicating that it was interrupted after signal()) {

  Set the interrupt flag to REINTERRUPT, reset the interrupt flag

}

if (the next waiting node of the node is not empty, indicating that it has not been disconnected from the condition queue) {//The cause is because the normal signal() will be directly disconnected from the condition queue, and the interruption occurs before the signal() Will be added to the blocking queue, causing no disconnection

  Disconnect from the conditional queue;

}

if (interrupt flag is not 0) {

  Throw an exception or interrupt the current thread again, or do nothing; //see 7 for details

}

2. addConditionWaiter() adds the current thread to the condition queue

 

 

Node t = the end of the conditional queue;

if (the end of the team is not empty && the end of the team state is not CONDITION) {

  Perform cleanup, clear all unqueued nodes in the conditional queue

  t = the tail after cleaning

}

Node node = Create a node for the current thread

if (the end of the team is empty) {

  Let the newly created node be the head node

}else {

  node enters the end of the queue

}

 

3. fullyRelease() passes in the current node, releases the lock held, and returns the value of the previous state

  

boolean failed = true; failed by default

try{

  Get the current state value  

  if (release lock successfully) {//see 4 for details

    Return state value before release

  } Otherwise throw an exception

}finally {

  If the release of the lock fails, the waitStatus of the current node = cancel the queue

}

 

4. Tell me in detail about release() passing in the state value before release to release the lock

 

 

 

 

 

 Try to release the lock

int c = current state-incoming state

if (if the thread holding the lock is not the current thread) throw an exception

if (c = 0) {

  Set the thread holding the lock to null

}

Set state to c

Return successful release or failure

 

5. checkInterruptWhileWaiting() is passed to the current node to check whether it is interrupted when suspended

 

 

If the thread is not interrupted during await, return 0 

 

If the thread is interrupted, is it interrupted before signal() wakes up, or afterwards 

The return of the interrupt before waking up THROW_IE throws an exception, indicating that it was interrupted before signal()

Interrupt return after waking up REINTERRYPT Reset the interrupt status (because there is no interrupt during await, the interrupt occurs after signal)

 

 

6. transferAfterCancellWait() cancel the transfer after suspension

 

 

 if (trying CAS to set the waitStatus of the current node to 0 means that the interrupt occurred before signal(), because signal will set waitStatus to 0) {

  When an interruption occurs, the current node is also added to the blocking queue

  Return true, transfer is successful

}

//This shows that the above CAS failed because signal() has set waitStatus to 0

while (the current node is not on the blocking queue) {

  //It may be that after the signal has not been transferred to the blocking queue, wait for a while in the loop
  Thread.yield();

}

return false;

Here is a scene depicted. Originally, there was a thread, which was ranked behind the conditional queue, but because it was interrupted, it would be awakened, and then it found that it was not the one being signaled, but it would take the initiative to go. Enter the blocking queue.

 

7. reportInterruptAfterWait() throws an exception or terminates the current thread again, or does nothing

 

 if (interrupt flag==THROW_IE){

  Throw interrupt exception

}else if(interrupt flag==RENITERRUPT){

  Self-interruption

}

Guess you like

Origin blog.csdn.net/a159357445566/article/details/109230522