深入理解 JUC:AQS 队列同步器( 六 )
方法 ConditionObject#signal 的实现如下:
public final void signal() {// 先检测当前线程是否获取到了锁 , 否则不允许继续执行if (!isHeldExclusively()) {throw new IllegalMonitorStateException();}// 获取条件队列头结点 , 即等待时间最长的结点Node first = firstWaiter;if (first != null) {// 将头结点从条件队列转移到同步队列 , 参与竞争资源this.doSignal(first);}}
调用 ConditionObject#signal 方法的线程必须位于临界区 , 也就是必须先持有独占锁 , 所以上述方法一开始会对这一条件进行校验 , 方法 AbstractQueuedSynchronizer#isHeldExclusively 是一个模板方法 , 交由子类来实现 。 如果满足执行条件 , 则上述方法会调用 ConditionObject#doSignal 方法将条件队列的头结点从条件队列转移到同步队列 。
private void doSignal(Node first) {// 从前往后遍历 , 直到遇到第一个不为 null 的结点 , 并将其从条件队列转移到同步队列do {if ((firstWaiter = first.nextWaiter) == null) {lastWaiter = null;}first.nextWaiter = null;} while (!transferForSignal(first) }// AbstractQueuedSynchronizer#transferForSignalfinal boolean transferForSignal(Node node) {// 更新当前结点的等待状态:CONDITION -> 0if (!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 = this.enq(node);int ws = p.waitStatus;// 如果前驱结点被取消 , 或者设置前驱结点的状态为 SIGNAL 失败 , 则唤醒当前结点上的线程if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL)) {LockSupport.unpark(node.thread);}return true;}
方法 ConditionObject#doSignal 会从前往后遍历条件队列 , 寻找第一个不为 null 的结点 , 并应用 AbstractQueuedSynchronizer#transferForSignal 方法尝试将其从条件队列转移到同步队列 。
在入同步队列之前 , 方法 AbstractQueuedSynchronizer#transferForSignal 会基于 CAS 机制清除结点的 CONDITION 状态 , 如果清除失败则说明该结点上的线程已被取消 , 此时 ConditionObject#doSignal 方法会继续寻找下一个可以被唤醒的结点 。 如果清除结点状态成功 , 则接下来会将该结点添加到同步队列的末端 , 同时依据前驱结点的状态决定是否唤醒当前结点上的线程 。
继续来看 ConditionObject#signalAll 方法的实现 , 相对于上面介绍的 ConditionObject#signal 方法 , 该方法的特点在于它会唤醒条件队列中所有不为 null 的等待结点 。 方法实现如下:
public final void signalAll() {if (!isHeldExclusively()) {// 先检测当前线程是否获取到了锁 , 否则不允许继续执行throw new IllegalMonitorStateException();}// 获取条件队列头结点Node first = firstWaiter;if (first != null) {// 将所有结点从条件队列转移到同步队列 , 参与竞争资源this.doSignalAll(first);}}private void doSignalAll(Node first) {lastWaiter = firstWaiter = null;do {Node next = first.nextWaiter;first.nextWaiter = null;transferForSignal(first);first = next;} while (first != null);}
实际上理解了 ConditionObject#doSignal 的运行机制 , 再理解 ConditionObject#signalAll 的运行机制也是水到渠成的事情 。
推荐阅读
- 全新8核国产CPU深入探秘:马上能买到
- 数据|新基建时代,高大全的数据管理解决方案是怎样“炼”成的?
- 16G运存+256G内存,专业骁龙865旗舰,性价比深入人心
- 深入理解Netty编解码、粘包拆包、心跳机制
- 绿色骑行深入校园,共享单车长途长时需求量提升
- 不被理解的超时代发明,你知道几个?在线膜拜大神,西瓜视频真相
- 深入调查SolarWinds黑客事件 微软已查封一个核心服务器
- 《深入理解Java虚拟机》:Java内存区域
- 深入探讨 JavaScript 逻辑赋值运算符
- 彻底理解 IO 多路复用实现机制