深入理解 JUC:AQS 队列同步器( 九 )
上述方法首先会获取前驱结点的等待状态 , 并依据具体的状态值进行决策:
- 如果前驱结点等待状态为 SIGNAL , 则说明当前结点需要被阻塞 , 所以直接返回 true;
- 否则 , 如果前驱结点的等待状态大于 0(即处于取消状态) , 则一直往前寻找未被取消的结点 , 并将当前结点排在其后 , 这种情况下直接返回 false , 再次尝试获取一次资源;
- 否则 , 前驱结点的状态为 0 或 PROPAGATE(不可能为 CONDITION 状态 , 因为当前处于同步队列) , 因为当前结点需要一个唤醒信号 , 所以修改前驱结点的状态为 SIGNAL , 这种情况下同样返回 false , 以再次确认不能获取到资源 。
如果在自旋期间发生异常 , 则上述方法会执行 AbstractQueuedSynchronizer#cancelAcquire 以取消当前结点等待获取资源的进程 , 包括设置结点的等待状态为 CANCELLED , 唤醒后继结点等 。
独占释放资源针对独占模式释放资源 , AbstractQueuedSynchronizer 定义了单一实现 , 即 AbstractQueuedSynchronizer#release 方法 , 该方法本质上是一个调度的过程 , 具体释放资源的操作交由 tryRelease 方法完成 , 由子类实现 。 方法 AbstractQueuedSynchronizer#release 实现如下:
public final boolean release(int arg) {// 尝试释放资源if (this.tryRelease(arg)) {Node h = head;// 如果释放资源成功 , 则尝试唤醒后继结点if (h != null}return true;}return false;}
如果 tryRelease 释放资源成功 , 则上述方法会尝试唤醒同步队列中由后往前距离头结点最近的一个结点上的线程 。 方法 AbstractQueuedSynchronizer#unparkSuccessor 的实现如下:private void unparkSuccessor(Node node) {// 获取当前结点状态int ws = node.waitStatus;if (ws < 0) {// 如果当前结点未被取消 , 则基于 CAS 更新结点等待状态为 0compareAndSetWaitStatus(node, ws, 0);}/** Thread to unpark is held in successor, which is normally just the next node.* But if cancelled or apparently null, traverse backwards from tail to find the actual non-cancelled successor.*/Node s = node.next; // 获取后继结点// 如果后继结点为 null , 或者被取消if (s == null || s.waitStatus > 0) {s = null;// 从后往前寻找距离当前结点最近的一个未被取消的线程结点for (Node t = tail; t != nullt = t.prev) {if (t.waitStatus <= 0) {s = t;}}}// 唤醒结点上的线程if (s != null) {LockSupport.unpark(s.thread);}}
选举待唤醒线程结点的过程被设计成从后往前遍历 , 寻找距离当前结点最近的未被取消的结点 , 并调用 LockSupport 工具类唤醒结点上的线程 。那 为什么要设计成从后往前遍历同步队列呢 ?在 Doug Lea 大神的论文中给出了答案 , 摘录如下:
An AbstractQueuedSynchronizer queue node contains a next link to its successor. But because there are no applicable techniques for lock-free atomic insertion of double-linked listnodes using compareAndSet, this link is not atomically set as part of insertion; it is simply assigned: pred.next = node; after the insertion. This is reflected in all usages. The next link is treated only as an optimized path. If a node's successor does not appear to exist (or appears to be cancelled) via its next field, it is always possible to start at the tail of the list and traverse backwards using the pred field to accurately check if therereally is one.
推荐阅读
- 全新8核国产CPU深入探秘:马上能买到
- 数据|新基建时代,高大全的数据管理解决方案是怎样“炼”成的?
- 16G运存+256G内存,专业骁龙865旗舰,性价比深入人心
- 深入理解Netty编解码、粘包拆包、心跳机制
- 绿色骑行深入校园,共享单车长途长时需求量提升
- 不被理解的超时代发明,你知道几个?在线膜拜大神,西瓜视频真相
- 深入调查SolarWinds黑客事件 微软已查封一个核心服务器
- 《深入理解Java虚拟机》:Java内存区域
- 深入探讨 JavaScript 逻辑赋值运算符
- 彻底理解 IO 多路复用实现机制