JUC中的AQS底层详细超详解( 三 )

3. acquireQueued循环阻塞-竞争并在 "处于头节点时尝试获取资源->睡眠->唤醒“中循环 。
当已经跑完任务的线程释放资源时 , 会唤醒之前阻塞的线程 。
当被唤醒后 , 就会检查自己是不是头节点 , 如果不是 , 且认为可以阻塞 , 那就继续睡觉去了
(下面代码注释部分选自AQS(acquireQueued(Node, int) 3)–队列同步器 - 小窝蜗 - 博客园 (http://cnblogs.com) )
final boolean acquireQueued(final Node node, int arg) { // 标识是否获取资源失败 boolean failed = true; try { // 标识当前线程是否被中断过 boolean interrupted = false; // 自旋操作 for (;;) { // 获取当前节点的前继节点 final Node p = node.predecessor(); // 如果前继节点为头结点 , 说明排队马上排到自己了 , 可以尝试获取资源 , 若获取资源成功 , 则执行下述操作 if (p == head && tryAcquire(arg)) { // 将当前节点设置为头结点 setHead(node); // 说明前继节点已经释放掉资源了 , 将其next置空 , 好让虚拟机提前回收掉前继节点 p.next = null; // help GC // 获取资源成功 , 修改标记位failed = false; // 返回中断标记 return interrupted; } // 若前继节点不是头结点 , 或者获取资源失败 ,  // 则需要判断是否需要阻塞该节点持有的线程 // 若可以阻塞 , 则继续执行parkAndCheckInterrupt()函数 ,  // 将该线程阻塞直至被唤醒 // 唤醒后会检查是否已经被中断 , 若返回true , 则将interrupted标志置于true if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())interrupted = true; } } finally { // 最终获取资源失败 , 则当前节点放弃获取资源 if (failed) cancelAcquire(node); } }4.shouldParkAfterFailedAcquire 检查是否可以阻塞该方法不会直接阻塞线程 , 因为一旦线程挂起 , 后续就只能通过唤醒机制 , 中间还发生了内核态用户态切换 , 消耗很大 。
因此会先不断确认前继节点的实际状态 , 在只能阻塞的情况下才会去阻塞 。
并且会过滤掉cancel的线程节点
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { // 获取前继节点的等待状态 int ws = pred.waitStatus; // 如果等待状态为Node.SIGNAL(-1) , 则直接返回true即可以阻塞 // 因为这说明前继节点完成资源的释放或者中断后 , 会主动唤醒后继节点的(这也即是signal信号的含义) , 因此方法外面不用再反复CAS了 , 直接阻塞吧 if (ws == Node.SIGNAL) return true; // 如果前继节点的等待值大于0即CANCELLED(1),说明前继节点的线程发生过cancel动作 // 那就继续往前遍历 , 直到当前节点的前继节点的状态不为cancel if (ws > 0) { do { node.prev = pred = pred.prev; } while (pred.waitStatus > 0); pred.next = node; } else { // 前继节点的等待状态不为SIGNAL(-1) , 也不为Cancel(1) // 那么只能是PROPAGATE(-3)或者CONDITION(-2)或者INITIAL(0) // 直接设置成SIGNAL , 下一次还没CAS成功 , 就直接睡觉了 // 因此在前面所有节点没辩护的情况下 ,  最多一次之后就会返回true让外面阻塞 compareAndSetWaitStatus(pred, ws, Node.SIGNAL); } return false;}5.parkAndCheckInterrupt() 阻塞线程使用LockSupport.park来阻塞当前这个对象所在的线程
private final boolean parkAndCheckInterrupt() { LockSupport.park(this);// 确认是否是中断导致的park结束 , 并清除中断标记 return Thread.interrupted();}public static void park(Object blocker) { Thread t = Thread.currentThread(); setBlocker(t, blocker); UNSAFE.park(false, 0L); setBlocker(t, null);}

经验总结扩展阅读