下面我们看下 JDK 关于AQS 抽线队列同步器中Node 的解释:

    1. 等待队列节点类。
    2. 等待队列是“CLH”(CraigLandin Hagersten)锁定队列的变体。 CLH 锁通常用于自旋锁。相反,我们将它们用于阻塞同步器,但使用相同的基本策略,即在其节点的前身中保存有关线程的一些控制信息。每个节点中的“状态”字段跟踪线程是否应该阻塞。节点在其前身释放时发出信号。否则,队列的每个节点都用作持有单个等待线程的特定通知样式的监视器。状态字段不控制线程是否被授予锁等。线程可能会尝试获取队列中的第一个线程。但成为第一并不能保证成功;它只赋予抗争的权利。所以当前释放的竞争者线程可能需要重新等待。
    3. 要加入 CLH 锁,您可以将其自动拼接为新的尾部。要出列,您只需设置 head 字段。
    4. +------+ prev +-----+ +-----+
    5. head | | <---- | | <---- | | tail
    6. +------+ +-----+ +-----+
    7. 插入 CLH 队列只需要对“尾”进行单个原子操作,因此从未排队到排队有一个简单的原子分界点。同样,出队只涉及更新“头”。然而,节点需要做更多的工作来确定他们的继任者是谁,部分原因是为了处理由于超时和中断而可能导致的取消。
    8. prev”链接(未在原始 CLH 锁中使用)主要用于处理取消。如果一个节点被取消,它的后继者(通常)重新链接到一个未取消的前任者。有关自旋锁情况下类似机制的解释,请参阅 Scott Scherer 的论文,网址为 http://www.cs.rochester.edu/u/scott/synchronization/
    9. 我们还使用“下一个”链接来实现阻塞机制。每个节点的线程 id 保存在它自己的节点中,因此前驱节点通过遍历下一个链接来确定它是哪个线程来通知下一个节点唤醒。继任者的确定必须避免与新排队的节点竞争以设置其前任者的“下一个”字段。当节点的后继节点似乎为空时,通过从原子更新的“尾部”向后检查来解决这个问题。 (或者,换一种说法,下一个链接是一种优化,因此我们通常不需要向后扫描。)
    10. 取消对基本算法引入了一些保守性。由于我们必须轮询其他节点的取消,我们可能会错过注意到取消的节点是在我们前面还是在我们后面。这是通过在取消时始终取消停放继任者来处理的,从而使他们能够稳定在新的前任上,除非我们能够确定一个未取消的前任来承担这一责任。
    11. CLH 队列需要一个虚拟头节点才能启动。但是我们不会在构建时创建它们,因为如果从不存在争用,那将是浪费精力。相反,在第一次争用时构造节点并设置头和尾指针。
    12. 等待条件的线程使用相同的节点,但使用附加链接。条件只需要链接简单(非并发)链接队列中的节点,因为它们仅在独占时才被访问。在等待时,将一个节点插入到条件队列中。收到信号后,节点被转移到主队列。状态字段的特殊值用于标记节点在哪个队列上。