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
}