深入理解 JUC:AQS 队列同步器( 八 )
方法 AbstractQueuedSynchronizer#addWaiter 用于将当前线程对象封装成结点添加到同步队列末端 , 并最终返回线程结点对象:
private Node addWaiter(Node mode) {// 为当前线程创建结点对象Node node = new Node(Thread.currentThread(), mode);// 基于 CAS 机制尝试快速添加结点到同步队列末端Node pred = tail;if (pred != null) {node.prev = pred;if (this.compareAndSetTail(pred, node)) {pred.next = node;return node;}}// 快速添加失败 , 继续尝试将该结点添加到同步队列末端 , 如果同步队列未被初始化则执行初始化this.enq(node);// 返回当前线程对应的结点对象return node;}
上述方法在添加结点的时候 , 如果同步队列已经存在 , 则尝试基于 CAS 操作快速将当前结点添加到同步队列末端 。 如果添加失败 , 或者队列不存在 , 则需要再次调用 AbstractQueuedSynchronizer#enq 方法执行添加操作 , 该方法在判断队列不存在时会初始化同步队列 , 然后基于 CAS 机制尝试往同步队列末端插入线程结点 。 方法实现如下:
private Node enq(final Node node) {for (; ; ) {// 获取同步队列末尾结点Node t = tail;// 如果结点不存在 , 则初始化if (t == null) { // Must initializeif (this.compareAndSetHead(new Node())) {tail = head;}} else {// 往末尾追加node.prev = t;if (this.compareAndSetTail(t, node)) {t.next = node;return t;}}}}
完成了结点的入同步队列操作 , 接下来会调用 AbstractQueuedSynchronizer#acquireQueued 方法基于自旋机制等待获取资源 , 在等待期间并不会响应中断 , 而是记录中断标志 , 等待获取资源成功后延迟响应 。 方法实现如下:
final boolean acquireQueued(final Node node, int arg) {boolean failed = true;try {boolean interrupted = false; // 标记自旋过程中是否被中断// 基于自旋机制等待获取资源for (; ; ) {// 获取前驱结点final Node p = node.predecessor();// 如果前驱结点为头结点 , 说明当前结点是排在同步队列最前面 , 可以尝试获取资源if (p == head// 头结点一般记录持有资源的线程结点p.next = null; // help GCfailed = false;return interrupted; // 自旋过程中是否被中断}// 如果还未轮到当前结点 , 或者获取资源失败if (shouldParkAfterFailedAcquire(p, node) // 判断是否需要阻塞当前线程}}} finally {// 尝试获取资源失败 , 说明执行异常 , 取消当前结点获取资源的进程if (failed) {this.cancelAcquire(node);}}}
上述方法会循环检测当前结点是否已经排在同步队列的最前端 , 如果是则调用 AbstractQueuedSynchronizer#tryAcquire 方法尝试获取资源 , 具体获取资源的过程由子类实现 。 自旋期间如果还未轮到调度当前线程结点 , 或者尝试获取资源失败 , 则会调用 AbstractQueuedSynchronizer#shouldParkAfterFailedAcquire 方法检测是否需要阻塞当前线程 , 具体判定的过程依赖于前驱结点的等待状态 , 实现如下:
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {// 获取前驱结点状态int ws = pred.waitStatus;if (ws == Node.SIGNAL) {// 前驱结点状态为 SIGNAL , 说明当前结点需要被阻塞return true;}if (ws > 0) {// 前驱结点处于取消状态 , 则一直往前寻找处于等待状态的结点 , 并排在其后面do {node.prev = pred = pred.prev;} while (pred.waitStatus > 0);pred.next = node;} else {/** 前驱结点的状态为 0 或 PROPAGATE , 但是当前结点需要一个被唤醒的信号 ,* 所以基于 CAS 将前驱结点等待状态设置为 SIGNAL , 在阻塞之前 , 调用者需要重试以再次确认不能获取到资源 。*/compareAndSetWaitStatus(pred, ws, Node.SIGNAL);}return false;}
推荐阅读
- 全新8核国产CPU深入探秘:马上能买到
- 数据|新基建时代,高大全的数据管理解决方案是怎样“炼”成的?
- 16G运存+256G内存,专业骁龙865旗舰,性价比深入人心
- 深入理解Netty编解码、粘包拆包、心跳机制
- 绿色骑行深入校园,共享单车长途长时需求量提升
- 不被理解的超时代发明,你知道几个?在线膜拜大神,西瓜视频真相
- 深入调查SolarWinds黑客事件 微软已查封一个核心服务器
- 《深入理解Java虚拟机》:Java内存区域
- 深入探讨 JavaScript 逻辑赋值运算符
- 彻底理解 IO 多路复用实现机制