Condition

181 阅读4分钟

1.Condition

1.1Condition概述

位于JUC包中,用来对原生的wait、notify/notifyAll这些方法进行增强,从Java语言层面,实现类似的功能。其方法的命名也类似于wait/notify。

image.png

在AQS中有一个同步等待队列(双向队列) ,而在Condition中也有一个条件队列(单向队列)有一个Condition对象就表示有一个条件队列

Object的wait和notify/notifyAll是与对象监视器配合完成的线程间的等待/通知机制。而Condition是和Lock配合完成等待通知机制。前者是Java底层级别的(wait/notify/notifyAll方法都是native方法,c++编写的,并且是由final修饰的,不可重写)。而后者是Java代码级别的实现,具体的实现在AQS中公共内部类ConditionObject中。

image.png

其中,属性firstWaiter表示条件队列的头结点(和AQS的同步等待对列不同的是:AQS初始化时的头结点是不封装线程的,也就是一个虚拟的头结点。但是在条件队列中初始化时的头结点是包含线程的,是从等待队列中出队后,再进入到条件队列中的。下面会进行具体的说明)。

lastWaiter是条件队列的尾结点。这和等待队列中的headtail类似。此外,Condition是基于AQS的独占锁实现,没有共享锁实现。

1.3Condition的源码分析

1.3.1await()

在我们使用Condition时,一般会通过Lock.newCondition来得到一个Condition对象。然后当前线程再获取锁,当满足一定条件,线程需要等待时,那么调用await()。如图:

image.png

从上图可以看出,在调用await方法后,首先检查当前线程的中断状态,如果为true,那么就会抛出异常

如果没有中断,才会将调用addConditionWaiter()方法把当前线程封装成一个结点,加入到条件队列的尾部。如果此时还没有同步队列,那么就在这个方法中初始化同步队列。初始化同步队列的方法也很简单:将当前线程封装成一个结点,并将firstWaiterlastWaiter都指向这个结点。如果条件队列不为空,在这个过程中,也会对条件队列进行维护,如图:

image.png

这是在addConditionWaiter中调用的一个方法,可以看到在这个方法中会遍历条件队列中结点,如果发现结点的waitStatus!=CONDITION,那么就会将这个结点从条件对列中移除。

在将结点加入到条件队列中后,又会将当前线程所占有的锁释放,如果释放失败,那么就会抛出 IllegalMonitorStateException异常。所以,在使用Condition中的awaitsignal方法时,也和wait/notify一样,都需要获取到锁才能调用,不然就会出现异常。

image.png

在线程释放锁后,就会将线程挂起。如果想要线程退出这种状态,那么有两种方法:

  1. 线程重新回到同步等待队列中
  2. 线程发生了中断,break退出循环。

image.png

但是,要走这个循环,那么首先线程需要被允许调度,那么就要调用signal(),那么就将线程唤醒,再判断是否中断,如果中断,那么也会将结点加入到等待队列中。

跳出循环后,就调用AQSacquireQueued()尝试排队获取锁,前面有节点,获取不到锁,就在等待队列中挂起。这就完成了出等到队列又回到等待队列的过程。

1.3.2 signal()

signal()方法就相对比较简单了。

image.png

首先先检查这个线程是不是持有锁,就是判断exclusiveOwnerThread保存的线程是不是当前线程,不是就抛出异常。所以,这里也可以体现出Conditionawait/signal也是需要锁的。如果线程持有锁,那么使用自旋的CAS操作将这个结点的waitStatusCONDITION修改为0

image.png

如上图,将头结点出队后,循环调用transferForSignal(),修改waitStatus的值。修改成功,将这个条件队列中的头结点加入到等待队列中,并且把waitStatus的值修改为SIGNAL,最后唤醒这个结点的线程。这个结点的线程就会从await的循环中出来,执行下面的流程。

1.3.3 signalAll

image.png

signalAll就是将条件队列中的结点全部出队,加入到等待队列中。

1.3.4 不可中断的方法

image.png

awaitUninterruptibly方法仅仅只是改变中断标志,不会进行什么实际的操作。

1.3.5超时等待的方法

image.png 如果时间到了,那么立马将这个结点从条件队列中加入到等待队列中,和wait相似。同时也是支持中断的