Condition
简介
Condition
的await()
/signal()
/signalAll()
方法提供了Object
的wait()
/notify()
/notifyAll()
方法的替代品;
此外,还对其进行了增强:一个锁可以有多个Condition
队列,以实现精准的唤醒。
本文主要通过源码来看看Condition
在AQS
里是怎么实现的。
在AQS
中,ConditionObject
实现了Serializable
接口,但是所有字段都是transient
的,因此一旦反序列化,等待队列将会清空。
主要属性有四个:
1 |
|
代码分析
等待方法 await
等待方法有四个版本,分别是:
- await():无超时机制,可中断
- awaitNanos(long):有超时机制,可中断
- awaitUntil(Date):有超时机制,可中断
- await(long, TimeUnit):有超时机制,可中断
- awaitUninterruptibly():无超时机制,不可中断
下文以第1个无超时机制、可中断的等待方法为例来看一下代码,其他的方法后面再谈。
await()
1 |
|
addConditionWaiter()
将当前线程包装进node,并移入条件队列
1 |
|
unlinkCancelledWaiters()
清除条件队列中取消的节点,纯链表操作
1 |
|
fullyRelease()
完全释放锁,并返回当前state值。
1 |
|
isOnSyncQueue()
判断是否已在等待队列中
1 |
|
findNodeFromTail()
从尾部遍历等待队列,判断是否在队列中
1 |
|
中断检查及处理
被唤醒后先进行中断检查,如果没有中断且还没被移入等待队列(意外唤醒),就将继续挂起; 否则就退出循环,根据中断类型进行处理了。
1 |
|
检查在条件队列中排队时的中断情况:
- 在
signal
之前被中断,返回THROW_IE
; - 在
signal
之后被中断,返回REINTERRUPT
; - 未被中断,返回0
1 |
|
1 |
|
根据中断类型分别处理:
- THROW_IE:抛出异常
- REINTERRUPT:标记一下中断标记
1 |
|
通知方法 signal
通知方法有通知1个和全部通知中两种:
- signal()
- signalAll()
两者的区别就在于将条件队列队头节点移到等待队列,或者是将所有节点都移到等待队列。 下文以signal()为例,分析一下代码:
signal()
1 |
|
transferForSignal()
1 |
|
这个时候唤醒节点做什么?
在AQS源码分析(1)——从ReentrantLock初窥AQS中的 2.1.6 AQS的acquireQueued()方法
里其实提到过,
在排队中被唤醒,会尝试去获取锁,如果获取失败,就会去挂起,在挂起之前会将前驱节点的等待状态设置为SIGNAL的。