深入理解 JUC:AQS 队列同步器( 二 )


public static void park(Object blocker) {// 获取当前线程对象Thread t = Thread.currentThread();// 记录当前线程阻塞等待的锁对象(设置线程对象的 parkBlocker 为参数指定的 blocker 对象)setBlocker(t, blocker);// 阻塞线程UNSAFE.park(false, 0L);// 线程恢复运行 , 清除 parkBlocker 参数记录的锁对象setBlocker(t, null);}具体实现比较简单 , 阻塞线程的操作依赖于 Unsafe 类实现 。 上述方法会调用 LockSupport#setBlocker 方法基于 Unsafe 类将参数指定的 blocker 对象记录到当前线程对象的 Thread#parkBlocker 字段中 , 然后进入阻塞状态 , 并在被唤醒之后清空对应的 Thread#parkBlocker 字段 。
当一个线程调用 park 方法进入阻塞状态之后 , 会在满足以下 3 个条件之一时 从阻塞状态中苏醒:

  1. 其它线程调用 unpark 方法唤醒当前线程 。
  2. 其它线程中断了当前线程的阻塞状态 。
  3. 方法 park 因为一些不合逻辑的原因退出 。
线程在从 park 方法中返回时并不会携带具体的返回原因 , 调用者需要自行检测 , 例如再次检查之前调用 park 方法的条件是否仍然满足以予以推测 。
方法 LockSupport#unpark 的实现同样基于 Unsafe 类实现 , 不同于 park 的多版本实现 , LockSupport 针对 unpark 方法仅提供了单一实现 , 如下:
public static void unpark(Thread thread) {if (thread != null) {UNSAFE.unpark(thread);}}需要注意的一点是 , 如果事先针对某个线程调用了 unpark 方法 , 则该线程继续调用 park 方法并不会进入阻塞状态 , 而是会立即返回 , 并且 park 方法是不可重入的 。
同步队列同步队列的作用在于管理竞争资源的线程 , 当一个线程竞争资源失败会被记录到同步队列的末端 , 并以自旋的方式循环检查能够成功获取到资源 。 AQS 的同步队列基于 CLH(Craig, Landin, and Hagersten) 锁思想进行设计和实现 。 CLH 锁是一种基于链表的可扩展、高性能 , 且具备公平性的自旋锁 。 线程以链表结点的形式进行组织 , 在等待期间相互独立的执行自旋 , 并不断轮询前驱结点的状态 , 如果发现前驱结点上的线程释放了资源则尝试获取 。
CLH 锁 是 AQS 队列同步器实现的基础 , AQS 以内部类 Node 的形式定义了同步队列结点 , 包括下一小节介绍的条件队列 , 同样以 Node 定义结点 。 Node 的字段定义如下:
static final class Node {/** 模式定义 */static final Node SHARED = new Node();static final Node EXCLUSIVE = null;/** 线程状态 */static final int CANCELLED = 1;static final int SIGNAL = -1;static final int CONDITION = -2;static final int PROPAGATE = -3;/** 线程等待状态 */volatile int waitStatus;/** 前驱结点 */volatile Node prev;/** 后置结点 */volatile Node next;/** 持有的线程对象 */volatile Thread thread;/** 对于独占模式而言 , 指向下一个处于 CONDITION 等待状态的结点;对于共享模式而言 , 则为 SHARED 结点 */Node nextWaiter;// ... 省略方法定义}由上述字段定义可以看出 , 位于 CLH 链表中的线程以 2 种模式在等待资源 , 即 SHARED 和 EXCLUSIVE , 其中 SHARED 表示共享模式 , 而 EXCLUSIVE 表示独占模式 。 共享模式与独占模式的主要区别在于 , 同一时刻独占模式只能有一个线程获取到资源 , 而共享模式在同一时刻可以有多个线程获取到资源 。 典型的场景就是读写锁 , 读操作可以有多个线程同时获取到读锁资源 , 而写操作同一时刻只能有一个线程获取到写锁资源 , 其它线程在尝试获取资源时都会被阻塞 。
AQS 的 CLH 锁 为处于 CLH 链表中的线程定义了 4 种状态 , 包括 CANCELLED、SIGNAL、CONDITION , 以及 PROPAGATE , 并以 Node#waitStatus 字段进行记录 。 这 4 种状态的含义分别为:


推荐阅读