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


  • CANCELLED :表示当前线程处于取消状态 , 一般是因为等待超时或者被中断 , 处于取消状态的线程不会再参与到竞争中 , 并一直保持该状态 。
  • SIGNAL:表示当前结点后继结点上的线程正在等待被唤醒 , 如果当前线程释放了持有的资源或者被取消 , 需要唤醒后继结点上的线程 。
  • CONDITION :表示当前线程正在等待某个条件 , 当某个线程在调用了 Condition#signal 方法后 , 当前结点将会被从条件队列转移到同步队列中 , 参与竞争资源 。
  • PROPAGATE :处于该状态的线程在释放共享资源 , 或接收到释放共享资源的信号时需要通知后继结点 , 以防止通知丢失 。
一个结点在被创建时 , 字段 Node#waitStatus 的初始值为 0 , 表示结点上的线程不位于上述任何状态 。
Node 类在方法定义上除了基本的构造方法外 , 仅定义了 Node#isShared 和 Node#predecessor 两个方法 , 其中前者用于返回当前结点是否以共享模式在等待 , 后者用于返回当前结点的前驱结点 。
介绍完了队列结点的定义 , 那么同步队列具体如何实现呢?这还需要依赖于 AbstractQueuedSynchronizer 类中的两个字段定义 , 即:
private transient volatile Node head;private transient volatile Node tail;其中 head 表示同步队列的头结点 , 而 tail 则表示同步队列的尾结点 , 具体组织形式如下图:
深入理解 JUC:AQS 队列同步器文章插图
当调用 AQS 的 acquire 方法获取资源时 , 如果资源不足则当前线程会被封装成 Node 结点添加到同步队列的末端 , 头结点 head 用于记录当前正在持有资源的线程结点 , 而 head 的后继结点就是下一个将要被调度的线程结点 , 当 release 方法被调用时 , 该结点上的线程将被唤醒 , 继续获取资源 。
关于同步队列结点入队列、出队列的实现先不展开 , 留到后面分析 AQS 资源获取与释放的过程时一并分析 。
条件队列除了上面介绍的同步队列 , 在 AQS 中还定义了一个条件队列 。 内部类 ConditionObject 实现了条件队列的组织形式 , 包含一个起始结点(firstWaiter)和一个末尾结点(lastWaiter) , 并同样以上面介绍的 Node 类定义结点 , 如下:
public class ConditionObject implements Condition, Serializable {/** 指向条件队列中的起始结点 */private transient Node firstWaiter;/** 指向条件队列的末尾结点 */private transient Node lastWaiter;// ... 省略方法定义}前面在分析 Node 内部类的时候 , 可以看到 Node 类还定义了一个 Node#nextWaiter 字段 , 用于指向条件队列中的下一个等待结点 。 由此我们可以描绘出条件队列的组织形式如下:
深入理解 JUC:AQS 队列同步器文章插图
ConditionObject 类实现了 Condition 接口 , 该接口定义了与 Lock 锁相关的线程通信方法 , 主要分为 await 和 signal 两大类 。
当线程调用 await 方法时 , 该线程会被包装成结点添加到条件队列的末端 , 并释放持有的资源 。 当条件得以满足时 , 方法 signal 可以将条件队列中的一个或全部的线程结点从条件队列转移到同步队列以参与竞争资源 。 应用可以创建多个 ConditionObject 对象 , 每个对象都对应一个条件队列 , 对于同一个条件队列而言 , 其中的线程所等待的条件是相同的 。
Condition 接口的定义如下:
public interface Condition {void await() throws InterruptedException;void awaitUninterruptibly();long awaitNanos(long nanosTimeout) throws InterruptedException;boolean await(long time, TimeUnit unit) throws InterruptedException;boolean awaitUntil(Date deadline) throws InterruptedException;void signal();void signalAll();}


推荐阅读