Condition源码解析

本文深入解析了Java并发编程中ReentrantLock的Condition接口,对比了它与Object监视器方法的差异。文章通过ArrayBlockingQueue示例展示了如何使用Condition,并详细介绍了Condition的实现原理,包括ConditionObject的内部结构、await方法、signal方法及其相关操作,如线程等待、唤醒等机制。
摘要由CSDN通过智能技术生成

描述

任意一个Java对象;都有一组监视器函数(定义在Object上),如wait()、wait(long timeout)、notify()以及notifyAll(),这些函数与synchronized同步关键字配合实现等待/通知模式。

Condition接口也提供了类似Object的监视器函数与Lock配合可以实现等待/通知模式,但是这两者在使用方式以及功能特性上还是有差别的。注意:只能独占锁才能使用Condition

Condition接口的默认实现为AQS(AbstractQueuedSynchronizer)中的ConditionObject.

以ReentrantLock锁实现为示例,下面来看看Condition的实现与使用。

Condition使用DEMO

//一个阻塞队列中使用Condition的示例代码。

class ArrayBlockingQueue{

  //保护所有操作的主锁

  final ReentrantLock lock;

  //poll操作的等待条件,队列为空时等待

  private final Condition notEmpty;

  //offer操作的等待条件,队列为满时等待

  private final Condition notFull;

  //生成实现并构建需要使用到的Condition.

  public ArrayBlockingQueue(int capacity) {

    items = new int[capacity];

    lock = new ReentrantLock(false);

    notEmpty = lock.newCondition();

    notFull = lock.newCondition();

  }

  //入队列,要使用Condition需要先加锁,然后使用Condition

  public boolean offer(int value) throws InterruptedException {

    //等待超时时间

    long nanos = TimeUnit.SECONDS.toNanos(3);

    final ReentrantLock lock = this.lock;

    lock.lockInterruptibly();

    try {

        CAS自旋,等待notFull的Condition可以使用(有超时判断,超过时间会响应中断).

        while (count == items.length) {

            if (nanos <= 0) {

                return false;

            }

            nanos = notFull.awaitNanos(nanos);

        }

        //执行入队列操作,到此步骤时表示队列可插入数据.

        enqueue(value);

        //唤醒一个等待notEmpty的Condition线程.

        notEmpty.signal();

        return true;

    }finally {

        lock.unlock();

    }

  }

  //有阻塞等待的poll操作,如果队列为空,等待3S后重试,(有超时判断,超过时间会响应中断).

  public int poll() throws InterruptedException {

    //等待超时时间

    long nanos = TimeUnit.SECONDS.toNanos(3);

    final ReentrantLock lock = this.lock;

    lock.lockInterruptibly();

    try {

        while (count == 0) {

            if (nanos <= 0) {

                return Integer.MIN_VALUE;

            }

            nanos = notEmpty.awaitNanos(nanos);

        }

        //执行出队操作,到此步骤表示队列中有数据可以取.

        int value = dequeue();

        //唤醒一个等待notFull的Condition线程.

        notFull.signal();

        return value;

    }finally {

        lock.unlock();

    }

  }

}

Condition实现

0,Condition结构

在AQS的ConditionObject实现中,维护有一个等待队列,并记录有队列的头尾节点.

//ConditionObject结构定义。

public class ConditionObject implements Condition, Serializable {

    /** First node of condition queue. */

    private transient Node firstWaiter; //链表头部节点

    /** Last node of condition queue. */ 

    private transient Node lastWaiter;//链表尾部节点

    public ConditionObject() { }

    ....

}

1,获取Condition实例

在ReentrantLock中通过newCondition获取一个Condition的实例。

//ReentrantLock中获取一个Condition的实例的实现。

final ConditionObject newCondition() {

    //直接new 一个AQS中的ConditionObject实例.

    return new ConditionObject();

}

2,await

await让线程等待分为两种情况,第一种是不带超时时间,第二种是带超时时间。

await不带超时时间

执行wait操作需要检查线程中断状态,如果线程中间,需要抛出异常,交给线程本身处理。

把线程添加到Condition的等待队列中(注意:不是锁的等待队列),并释放当前线程的锁资源,得到在释放锁资源以前原来锁的重入次数。

如果线程不在锁的等待队列中时(或不是队顶元素线程被signal唤醒会把线程添加到锁的等待队列中),让线程处于BLOCKED状态,当线程被signal唤醒后(此时线程会进入锁的等待队列,waitStatus被修改为0),并根据线程是否中断来考虑是否需要重新压入线程到锁的等待队列(线程中断需要压入到锁的等待队列)。

当线程被唤醒后,会通过“acquireQueued”尝试让线程获取锁,并判断线程是否中断(根据中断是否成功入队列来考虑是否需要重新设置线程的中断状态)

如果线程有后继节点,清理Condition等待队列中被取消的线程。

//ConditionObject.await,实现Condition的等待。

public final void await() throws InterruptedException {

    //如果当前线程处于中断状态,直接抛出中断信号,通知线程处理.

    if (Thread.interrupted())

        throw new InterruptedException();

    //把线程添加到等待队列尾部,线程节点的waitStatus状态为“CONDITION”.

    //==>每次添加线程到队列时,都会清理掉队列中非"CONDITION"的节点.

    //==>注意:这里的队列是Condition的等待队列,不是锁对应的等待队列.

    Node node = addConditionWaiter();

    //释放当前锁资源(让线程等待),并唤醒一个等待队列中的线程,

    //==>函数返回值是锁的重入次数(state),在唤醒时线程时会重新压入此state值.

    int savedState = fullyRelease(node);

    int interruptMode = 0;

    //如果节点(CONDITION)有后继节点或者在队列中,返回true.

    //首次执行时,线程还未添加到锁的队列中,原则上此时返回false.

    //只有线程被signal唤醒或中断,isOnSyncQueue才返回true.

    while (!isOnSyncQueue(node)) {

        //让线程进行BLOCKED状态,如果线程处于中断状态把当前线程添加到锁的等待队列中,

        LockSupport.park(this);

        //这里在线程唤醒后执行,如果线程处于中断状态,结束迭代.

        //==>checkInterruptWhileWaiting返回值0线程未中断,什么都不做.

        //====>线程如果处于中断状态:

        //======>返回值"-1"表示添加锁等待队列成功,并成功设置waitStatus设置为0.

        //======>返回值"1"表示添加锁等待队列失败,此时说明线程是处于唤醒状态,

        //=========>"Thread.yield"让出cpu执行时间. 

        if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)

            break;

    }

    //尝试让当前线程获取锁(在signal时会唤醒线程并添加到锁的等待队列).

    //如果线程成功获取锁,锁的state会重新设置为savedState,否则BLOCKED线程等待前继节点唤醒.

    //acquireQueued函数返回true表示线程被中断.

    //==>acquireQueued返回true,

    //====>"interruptMode != THROW_IE",说明线程中断重新入锁队列失败.

    if (acquireQueued(node, savedState) && interruptMode != THROW_IE) {

        //此时表示线程被中断,同时线程重入锁的等待队列失败,此时说明线程需要重入中断.

        interruptMode = REINTERRUPT;

    }

    //如果线程后继节点,清理Condition等待队列中被取消的线程.

    if (node.nextWaiter != null) // clean up if cancelled

        unlinkCancelledWaiters();

    //根据中断是否重入(线程不在锁等待队列中,表示可重入),处理中断信号.

    //==>不可重入时,抛出线程中断异常,否则重新设置线程的中断信号.

    if (interruptMode != 0)

        reportInterruptAfterWait(interruptMode);

}

await带超时时间:

带超时时间的await与不带超时的await不同处在于当达到指定超时时间后,线程会自动唤醒,而不是等待signal信号来唤醒,其它流程与await相同。

//ConditionObject.await,实现Condition的等待,带超时时间。

public final boolean await(long time, TimeUnit unit)

        throws InterruptedException {

    long nanosTimeout = unit.toNanos(time);

    //如果处理wait时,线程是中断状态,直接抛出异常,交由线程自己处理。

    if (Thread.interrupted())

        throw new InterruptedException();

    //把当前线程添加到Condition的等待队列中.

    Node node = addConditionWaiter();

    //释放当前线程的锁资源,并得到当前锁的重入次数.

    int savedState = fullyRelease(node);

    final long deadline = System.nanoTime() + nanosTimeout;

    boolean timedout = false;

    int interruptMode = 0;

    //如果节点(CONDITION)有后继节点或者在锁队列中,返回true.

    //首次执行时,线程还未添加到锁的队列中,原则上此时返回false.

    //只有线程被signal唤醒或中断,isOnSyncQueue才返回true.

    while (!isOnSyncQueue(node)) {

        //如果等待时间已经超过指定的时间线程被取消,

        //=>transferAfterCancelledWait函数

        //==>把线程添加到同步队列中(锁等待队列)结束当前的while迭代. 

        //==>如果超时取消发生在signal前(说明添加同步队列成功)返回true,

        //====>添加同步队列成功,waitStatus的值为0

        //==>如果signal发生成超时取消前,"Thread.yield"让出CPU执行时间,返回false.

        if (nanosTimeout <= 0L) {

            timedout = transferAfterCancelledWait(node);

            break;

        }

        //如果设置的超时时间超过1秒,直接使用LockSupport.parkNanos等待指定时间.

        //===>这种方法比CAS自旋更快(JDK文档说的)。

        if (nanosTimeout >= spinForTimeoutThreshold)

            LockSupport.parkNanos(this, nanosTimeout);

        //判断线程是否被中断,如果中断:尝试添加线程到同步队列.

        //==>(成功添加同步队列会设置waitStatus为0).

        //"interruptMode!=0"表示线程处于中断状态,成功添加到同步队列为-1,否则为1.

        //==>如果线程处于中断状态,结束当前迭代.

        if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)

            break;

        nanosTimeout = deadline - System.nanoTime();

    }

    //当线程被唤醒或者线程被中断,尝试获取线程锁,state会重新设置为savedState.

    //acquireQueued返回true表示线程被中断(请参考AQS.acquireQueued).

    //====>"interruptMode != THROW_IE",说明线程中断重新入锁队列失败. 

    if (acquireQueued(node, savedState) && interruptMode != THROW_IE) {

        //此时表示线程被中断,同时线程重入锁的等待队列失败,此时说明线程需要重入中断.

        interruptMode = REINTERRUPT;

    }

    //如果线程有后继节点,清理Condition等待队列中被取消的线程.

    if (node.nextWaiter != null)

        unlinkCancelledWaiters();

    //根据中断是否重入(线程不在锁等待队列中,表示可重入),处理中断信号.

    //==>不可重入时,抛出线程中断异常,否则重新设置线程的中断信号.

    if (interruptMode != 0)

        reportInterruptAfterWait(interruptMode);

    //返回true说明未超时,否则表示超时.

    return !timedout;

}

addConditionWaiter

添加线程到Condition的等待队列,此函数只会在await操作时会调用,向队列尾部添加当前线程节点。

//把当前线程添加到Condition的等待队列中。

//注意:Condition的队列操作并没有使用原子操作,而是直接赋值操作(因为只有当前线程拥有锁)

private Node addConditionWaiter() {

    Node t = lastWaiter;//lastWaiter是队列尾部节点

    // If lastWaiter is cancelled, clean out.

    //如果lastWaiter已经被取消,清理队列中已经取消的节点.

    if (t != null && t.waitStatus != Node.CONDITION) {

        unlinkCancelledWaiters();

        //重新刷新lastWaiter的值.

        t = lastWaiter;

    }

    //包装当前线程为一个CONDITION节点. 

    Node node = new Node(Thread.currentThread(), Node.CONDITION);

    //"t == null"表示队列还未初始化,设置队列的头节点为当前线程。

    //==>否则设置当前线程为lastWaiter的后继节点,并更新lastWaiter.

    if (t == null)

        firstWaiter = node;

    else

        t.nextWaiter = node;

    lastWaiter = node;

    return node;

}

fullyRelease

释放线程的锁资源,释放后,锁的state会被重置为0,此函数的返回值是当前线程的重入锁的数量。为什么会释放线程所有的锁资源?因为Condition.await时表示线程进行等待状态,CPU让给其它线程执行。

//释放当前线程的锁资源,此时会释放线程的所有重入锁。

//==>目前此函数只会在Condition.await时调用(让线程等待,释放线程锁)

final int fullyRelease(Node node) {

    boolean failed = true;

    try {

        //先得到重入锁的数据,

        int savedState = getState();

        //释放锁资源(请参考AQS.release),此时state的值变为0.

        if (release(savedState)) {

            failed = false;

            return savedState;

        } else {

            throw new IllegalMonitorStateException();

        }

    } finally {

        //如果释放锁资源失败,会标记此线程为CANCELLED状态. 

        if (failed)

            node.waitStatus = Node.CANCELLED;

    }

}

isOnSyncQueue

此函数判断Condition线程是否在锁同步队列中,如果线程的waitStatus是CONDITION或者node.prev为null时,表示不在队列中返回false,其它情况返回true.

//判断线程是否在锁的同步队列中:

//==>如果线程节点是“CONDITION”或者线程没有前继节点返回false。

//==>如果线程还有后继节点,返回true,此时线程必然在队列中.

//==>迭代同步队列,从队尾节点向前查找,如果找到节点返回true,否则返回false表示不在队列中.

final boolean isOnSyncQueue(Node node) {

    if (node.waitStatus == Node.CONDITION || node.prev == null)

        return false;

    if (node.next != null) // If has successor, it must be on queue

        return true;

    /*

     * node.prev can be non-null, but not yet on queue because

     * the CAS to place it on queue can fail. So we have to

     * traverse from tail to make sure it actually made it.  It

     * will always be near the tail in calls to this method, and

     * unless the CAS failed (which is unlikely), it will be

     * there, so we hardly ever traverse much.

     */

    return findNodeFromTail(node);

}

//判断线程节点是否在同步队列中。

private boolean findNodeFromTail(Node node) {

    Node t = tail;

    for (;;) {

        if (t == node)

            return true;

        if (t == null)

            return false;

        t = t.prev;

    }

}

checkInterruptWhileWaiting

判断线程是否中断,如果线程未中断,直接返回0,否则尝试添加线程到锁同步队列,如果成功添加到同步队列,返回"THROW_IE:-1"(表示需要抛出异常),否则"REINTERRUPT:1"表示需要重入中断,在线程中断尝试添加同步队列时(原子操作waitStatus设置为0失败),表示有线程signal唤醒了当前线程,那么需要让出当前线程CPU执行时间,让signal的线程有机会把当前线程添加到锁的同步队列中。

//判断线程是否有中断信息,如果有,尝试把线程添加到锁同步队列中,

//==>如果在添加到同步队列前,线程被signal唤醒,返回"REINTERRUPT:1"

//==>如果成功添加线程到锁同步队列,返回"THROW_IE:-1",此时线程的waitStatus值被设置为0

//==>如果线程未中断,返回0.

private int checkInterruptWhileWaiting(Node node) {

    return Thread.interrupted() ?

        (transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :

        0;

}

//尝试把已经中断的线程添加到锁同步队列中,

//==>如果成功添加到锁同步队列,设置线程的waitStatus为0.

//==>否则进行CAS自旋,让出CPU执行时间,等待signal唤醒线程后把线程添加到锁同步队列.

final boolean transferAfterCancelledWait(Node node) {

    if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {

        enq(node);

        return true;

    }

    /*

     * If we lost out to a signal(), then we can't proceed

     * until it finishes its enq().  Cancelling during an

     * incomplete transfer is both rare and transient, so just

     * spin.

     */

    while (!isOnSyncQueue(node))

        Thread.yield();

    return false;

}

unlinkCancelledWaiters

移出Condition等待队列中被取消的线程,并重新更新队列的头尾节点。

//从Condition的等待队列中移出被取消的线程,

private void unlinkCancelledWaiters() {

    Node t = firstWaiter;

    Node trail = null;

    //从队列的头部节点开始向下查找,

    //==>如果线程的waitStatue不是“CONDITION”状态,表示线程被取消,从队列中移出此节点.

    //==>移出节点后,重新更新队列的头尾节点.

    while (t != null) {

        Node next = t.nextWaiter;

        if (t.waitStatus != Node.CONDITION) {

            t.nextWaiter = null;

            if (trail == null)

                firstWaiter = next;

            else

                trail.nextWaiter = next;

            if (next == null)

                lastWaiter = trail;

        }

        else

            trail = t;

        t = next;

    }

}

signal

signal函数用于唤醒Condition等待队列中的队顶线程,此函数采用CAS自旋锁直到唤醒成功结束(或者等待队列为空),如果成功唤醒一个线程,会把线程添加到锁的同步队列中。

//Condition唤醒一个等待中的线程(与await对应),

public final void signal() {

    //判断当前线程是否锁持有者线程,只有持有锁的线程才能唤醒其它线程.

    //isHeldExclusively函数在ReentrantLock中实现(判断线程是否owner)

    if (!isHeldExclusively())

        throw new IllegalMonitorStateException();

    //唤醒Condition等待队列队顶线程。

    Node first = firstWaiter;

    //如果等待队列没有线程,啥都不做.

    if (first != null)

        doSignal(first);

}

//signal的具体唤醒函数,唤醒等待队列的队顶元素线程,并更新队列指针.

private void doSignal(Node first) {

    //CAS自旋,尝试唤醒队顶元素线程

    do {

        if ( (firstWaiter = first.nextWaiter) == null)

            lastWaiter = null;

        first.nextWaiter = null;

    } while (!transferForSignal(first) &&

             (first = firstWaiter) != null);

}

transferForSignal

此函数是Condition处理signal唤醒线程的具体实现,分为如下几种情况:

1,线程当前waitStatus状态不是”CONDITION”,说明线程被取消,直接返回false结束.

2,线程添加到锁同步队列,此时返回true.

2,1,判断线程的前继节点是否是取消状态,如果是直接唤醒线程。

2,2,尝试修改前继节点的waitStatus为”SIGNAL”,如果失败直接唤醒线程。

2,3,此时线程由锁同步队列中线程的前继节点来处理唤醒.

//Condition处理signal信号,即尝试唤醒等待中的线程,

//=>1,如果线程被取消(线程的waitStatus!=CONDITION),直接结束流程返回false.

//=>2,把线程添加到锁的同步队列中,并得到线程对应的前继节点,

//==>1,如果前继节点被取消或者尝试修改前继节点的wiatStatus为"SIGNAL"失败,

//======>直接唤醒node对应的线程(这种情况是暂时的说明前继节点有其它线程在处理waitStatus).

//==>2,添加线程到锁同步队列成功,此时对线程的前继节点设置有信号,表示前继节点需要唤醒线程.

final boolean transferForSignal(Node node) {

    /*

     * If cannot change waitStatus, the node has been cancelled.

     */

    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).

     */

    Node p = enq(node);

    int ws = p.waitStatus;

    if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))

        LockSupport.unpark(node.thread);

    return true;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值