takeaway

In this article we will look at the Condition implementation principle in ReentrantLock. After reading this article, you will know:

1. How is the underlying Condition implemented


There are the following reentrantLocks and conditions:

// Lock and condition variables
private final Lock lock = new ReentrantLock();
/ / conditions
private final Condition condition1 = lock.newCondition();
Copy the code

Let’s look at the process of executing await and signal.

1, await

Generally, the await method of condition can be used only after the thread has obtained the LOCK. Suppose thread 1 has acquired the ReentrantLock lock. When executing the code logic, it finds that some conditions are not met and calls the following code:

while{condition1.await(); }Copy the code

At this point, AQS mainly performs the following actions:

  1. Thread 1 wraps itself as a node, with waitStatus set to CONDITION(-2), and appends conditionqueue to ConditionObject (each ConditionObject has its own conditionqueue).
  2. Thread 1 releases the lock and sets state to 0;
  3. Then wake up the next node of the head node in the wait queue;

As follows:

If the node of the current thread is not in the queue, the current thread will be blocked as follows:

while(! isOnSyncQueue(node)) { LockSupport.park(this);
  if((interruptMode = checkInterruptWhileWaiting(node)) ! =0)
    break;
}
Copy the code

Condition1. signal or condition1.signalAll is used to make condition1.signal or condition1.signalAll.

2, signal wake up

When condition1.signal is executed by another thread, it basically does the following:

  1. Append the first node in the conditional queue to the wait queue;
  2. Set waitStatus on the end of the wait queue to SIGNAL.

When the head node is finished, the lock is released and the thread of the next node in the waiting queue is woken up to work.

3. Continue execution after await is restored

If the thread is awaked, the thread will continue to execute as if it were below the block in the await method. Here is the source code:

// If the current node is not in the wait queue, it will continue to block
while(! isOnSyncQueue(node)) { LockSupport.park(this);
  if((interruptMode = checkInterruptWhileWaiting(node)) ! =0)
    break;
}
// This method does the following:
// 1. Try to obtain the ReentrantLock
// 2. The current thread node becomes the new head node
// 3. Otherwise, continue to sleep as required
if(acquireQueued(node, savedState) && interruptMode ! = THROW_IE) interruptMode = REINTERRUPT;if(node.nextWaiter ! =null) // If the waiting node is cancelled, it is removed from the conditional queue
  unlinkCancelledWaiters();
if(interruptMode ! =0)
  reportInterruptAfterWait(interruptMode);
Copy the code

It can be found that it is mainly determined that the current thread node has been put into the wait queue, so it will try to obtain the lock, and then continue to execute the code.

In the first section, we learned that only after a thread has acquired the lock of a ReentrantLock can it continue to execute. In the middle, it may enter the conditional queue and release the lock because of await execution. Finally, it will wake up to acquire the lock again and continue executing. Finally, according to the written specification, we must execute reentrantLock. unlock() in the code to release the lock, and then continue to wake up the waiting queue for subsequent threads to continue executing.

Author: Arthinking

Blog links:

Condition principle analysis of ReentrantLock

Copyright notice: copyright belongs to the author, shall not be reproduced without permission, infringement will be investigated! Please add the public number to contact the author.