本文聊下JUC中的Condition使用,以及源码实现
什么是Condition
- Condition 实际是等待/通知模型的一种实现。
- 聊synchronized原理的时候,我们有聊到java中每个对象都有一个
monitor
, 被synchronized修饰的代码块/方法,在执行的时候,会先尝试获取到对应的monitor
,如果没有获取到,则使用wait
阻塞(重量级锁),待其他获取到monitor
的线程释放掉monitor
,调用notify
再尝试重新获取signal
。wait
/notify
方法就是所谓的等待/通知模型。 - synchronized 做为隐式的,有对应的显示实现Lock。
monitor
的等待/通知 ,同样有其对应的实现,即Condition接口。
Condition vs monitor
对比名称 | monitor | Condition |
---|---|---|
前置条件 | 获取到对象的锁 | 调用Lock.lock()获取到锁,Lock.newCondition创建Condition对象 |
调用方式 | object.wait/notify | condition.await/signal |
等待队列个数 | 1个 | 多个 |
是否支持等待中,不响应中断 | 不支持 | 支持 |
是否支持等待到某个具体的时间点 | 不支持 | 支持(awaitUtile接口) |
demo
构建一个有界队列
创建两个条件:notEmpty(非空) notFull(非满)
创建两个线程:一个生成,一个消费
1 | public class ConditionTest { |
原理剖析
Condition 接口
1 | public interface Condition { |
- Condition接口在JUC中的实现是AQS中的ConditionObject
ConditionObject
ConditionObject 实现Condition主要有三个点: 等待队列,等待,通知
等待队列
一个Condition 包含一个等待队列。
等待队列的节点是复用的AQS.Node
下面是ConditionObject 的属性
1
2
3
4/** 首节点 */
private transient Node firstWaiter;
/** 尾节点 */
private transient Node lastWaiter;等待队列
等待队列与AQS的关系
- 具体见下图
等待(await)
源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
// interrupt 是为了判断是中断异常,还是人为中断
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}流程图
注意
- 因为wait是在获取到锁之后才被调用的,所以没有用CAS
通知(signal)
源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public final void signal() {
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);
}流程图
参考大佬
- 《java并发编程的艺术》方腾飞 魏鹏 程晓明 著