Talk JUC ---------- Condition source code analysis

First, what is the Condition?

Previous multi-thread communication using the Object class provides waitand notifymethods, Conditionhave similar wait, notifyfunctions, and provides an interrupt when the thread whether to make the appropriate treatment. ConditionBased on the underlying FIFO queue, it must be combined with Locka lock used together, and Conditionexamples of the Lockcreated. Finally, summed up, Conditionit is a multi-threaded communications tools, shows a state of multi-thread in participation data competition, is responsible for suspend and resume work on the threads of a multithreaded environment.

Below posted a interception from "Art concurrent programming in Java," the comparison chart:

Two, Condition source code analysis

1, Condition architecture and its internal structure is like?

ConditionIt is an interface, jdk provides Conditionan implementation class ConditionObject, ConditionObjectis AQSin the inner class, because Conditionthe operation requires access to relevant locks, and AQSis the basis to achieve synchronization lock. ConditionProviding several operations following method:

  • void await() throws InterruptedException

When this method is called, the thread is suspended until the other thread wakes up. Note that when the suspended thread is interrupted, will throw InterruptedExceptionan exception.

  • void awaitUninterruptibly()

This approach with the above await()method is similar, except that this method does not handle the situation the thread interruption.

  • long awaitNanos(long nanosTimeout) throws InterruptedException

It represents the longest wait for a specified time, unless interrupted midway or early wake-up call, return value = nanosTimeout- have time to wait.

  • void awaitUninterruptibly()

This approach with the above await()method is similar, except that this method does not handle the situation the thread interruption.

  • boolean await(long time, TimeUnit unit) throws InterruptedException

The same awaitNanos(), except that you can specify the time units.

  • boolean awaitUntil(Date deadline) throws InterruptedException

It represents a thread hang, knowing some point wake up the thread.

  • void signal()as well asvoid signalAll()

Wake-up operation.

So the question is, how to obtain Conditionthe object of it? View Lockinterface, which declares a newCondition()method that returns an Conditionobject, such as Lockan implementation of the interface ReentrantLockin provides a realization of the method, but the essence is to create ConditionObjectobjects.

Source as follows:

public Condition newCondition() {
    return sync.newCondition();
}
复制代码

Which syncis inherited from AQSthe synchronizer.

final ConditionObject newCondition() {
    return new ConditionObject();
}
复制代码

About AQS, you can refer to the following article:

----- Java Concurrency the AQS (abstract queue synchronizer)

2, the internal await implementation details Condition

Below is await()a method source code:

public final void await() throws InterruptedException {
    // 1、线程如果中断,那么抛出异常
    if (Thread.interrupted())
        throw new InterruptedException();
    // 2、将当前线程包装成为一个Node节点,加入FIFO队列中
    Node node = addConditionWaiter();
    // 3、释放锁
    int savedState = fullyRelease(node);
    int interruptMode = 0;
    // 4、判断节点是否在同步队列(注意非Condition队列)中,如果没有,则挂起当前线程,因为该线程尚未具备数据竞争资格
    while (!isOnSyncQueue(node)) {
        // 5、挂起线程
        LockSupport.park(this);
        // 6、中断直接返回
        if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
            break;
    }
    // 7、参与数据竞争(非中断时执行)
    if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
        interruptMode = REINTERRUPT;
        
    // 清理条件队列中状态为cancelled的节点
    if (node.nextWaiter != null) // clean up if cancelled
        unlinkCancelledWaiters();
    
    if (interruptMode != 0)
        reportInterruptAfterWait(interruptMode);
    }
复制代码

We can see from the above code, because awaititself does not support interrupts, so if the current thread is interrupted, then the first step direct throw. The second step of the current thread packed into a node Node, was added to the Conditioncondition of the queue, Nodeand AQSuse the same type of view AQSunderstood, each Nodewith a current Threadassociated.

static final class Node {
    volatile Thread thread;
}
复制代码

The third step is then performed to release the lock, here it comes to why Conditionyou must get the problem when using the lock, below is fullyRelease()the source code:

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;
    }
}
复制代码

Step 4-6 is continuous cycle until the node appear in the synchronization queue, pay attention to the synchronization queue is not a queue condition, belonging to AQSa part of Condition, then this is how to determine whether the while loop in the synchronization queue it? Actually very simple, is that when the node into non-state CONDITIONon it, such as call signal(), but please note that LockSupport.park(this);after this code is executing the blocking code, huh, later analysis signalwill be mentioned. Source as follows:

final boolean isOnSyncQueue(Node node) {
    // 判断节点的状态是不是CONDITION,CONDITION表示该节点正在处于等待某个条件,此时就应该park挂起线程
    if (node.waitStatus == Node.CONDITION || node.prev == null)
        return false;
    if (node.next != null) // If has successor, it must be on queue
        return true;
    return findNodeFromTail(node);
}
复制代码

Step 7 is also well understood, since the conditions for the current thread to wait has been awakened, then the data directly involved in the competition to get away, step 8 to clean out some of the state cancelledof the node, due to a thread break or timeout, state of the node will be markedcancelled

3, the signal internal implementation details Condition
public final void signal() {
    // 1、必须获得锁
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    Node first = firstWaiter;
    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);
}
复制代码

Requirements Call of signal condition of the current thread has acquired the lock, which will make the wait queue head node that is the longest waiting time to synchronize the nodes into the queue, the queue after synchronization and moved to have the opportunity to make the waiting thread wake up, return (this) method, that is, from await methods LockSupport.park (this method is referred to as obstructive live current code above), which have a chance to make thread calls await method successfully quit

Third, the summary

Use lock combination Conditioncan solve the problem of producers and consumers, need to pay attention is to use Conditionmust first acquire the lock, otherwise the error, that is, generally in the following manner to call Condition.

lock.lock();
condition.await();

condition.signal();
lock.unlock();
复制代码

Guess you like

Origin juejin.im/post/5e2419796fb9a02fc90e3726