深入理解 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;}
- 设计模式6之代理模式
- 大牛深入解析SpringBoot核心运行原理和运作原理源码
- 一线大牛带你深入解析AutoConfiguration源码
- 更新了!深入浅出图解Git,入门到精通(保姆级教程)第三篇
- 《深入理解Java虚拟机》:锁优化
- 飞智对手游的理解或许领先行业一个时代,全新八爪鱼2代上手
- 理解JavaScript中的浅拷贝与深拷贝
- 怎么理解分布式、高并发、多线程
- 5分钟理解Redis持久化
- 深入了解一加8/8T系列 看完就知道该不该买