AbstractQueuedSynchronizer Node文档翻译

    一、概述  

 等待队列是“ CLH”(Craig,Landin和 Hagersten)锁定队列的变体。 CLH锁通常用于自旋锁。相反,我们使用它们来做阻塞同步器,但是使用相同的基本策略,即在其节点的前身中保存有关线程的某些控制信息。每个节点中的“状态”字段跟踪线程是否应阻塞。节点的前任释放时会发出信号。否则,队列的每个节点都充当特定通知样式的监视器,其中包含单个等待线程。虽然状态字段不控制线程是否被授予锁等。在队列中头节点线程可能会尝试获取,但是不能保证成功。 它仅授予竞争权。因此,当前被释放的竞争者线程可能需要重新等待。

      插入到CLH队列中,只需要对“尾巴”执行一次原子操作,因此存在一个简单的原子分界点,即从未排队到排队。同样,出队仅涉及更新“头”。但是,要确定节点的后继者是谁,需要花费更多的精力,部分原因是要处理由于超时和中断而可能导致的取消。

      “ prev”链接(在原始CLH锁中不使用)主要用于处理取消。如果取消某个节点,则其后继节点(通常)会重新链接到未取消的前任节点。有关自旋锁情况下类似机制的说明,请参见Scott和Scherer的论文,网址为http://www.cs.rochester.edu/u/scott/synchronization/。

      我们还使用“下一个”链接来实现阻塞机制。每个节点的线程ID都保留在其自己的节点中,因此前任通过遍历下一个链接以确定它是哪个线程,从而通知下一个节点唤醒。确定后继者必须避免与新排队的节点竞争来设置其前任节点的“ next”字段。在必要时,可以通过在节点的后继者似乎为空时从原子更新的“尾部”向后检查来解决此问题。 (或者换句话说,下一个链接是一种优化,因此我们通常不需要向后扫描。)

      取消将一些保守性引入了基本算法。由于我们必须轮询其他节点的取消,因此我们可能会遗漏一个被取消的节点在我们前面还是后面。要解决此问题,必须始终在取消合同时取消继任者,使他们能够稳定在新的前任身上,除非我们能够确定一个将要承担此责任的前任取消。

      CLH队列需要一个虚拟头节点节点才能开始。但是,我们不会在构建过程中创建它们,因为如果没有争用,这将是徒劳的。而是构造节点,并在第一次争用时设置头和尾指针。

      等待条件的线程使用相同的节点,但是使用附加的链接。条件只需要在简单(非并行)链接队列中链接节点,因为只有在专用时才可以访问它们。等待时,将节点插入条件队列。收到信号后,该节点将转移到主队列。状态字段的特殊值用于标记节点所在的队列。

二、属性

int waitStatus 表示节点的状态,包含的状态有 

状态 含义
SIGNAL -1 该节点的后继节点将要或者已经被阻塞,因此当前节点释放或取消时必须unpark其后继节点。为了避免竞争,acquire方法必须首先表明它们需要信号,然后重试原子获取,然后在失败时阻塞
CONDITION -2 该节点当前在condition队列中。 在传输之前,它不会用作同步队列节点,那时状态将设置为0。(此处使用此值与字段的其他用途无关,但简化了机制)
PROPAGATE -3 releaseShared应该传播到其他节点。在 doReleaseShared中设置了此设置(仅用于头节点),以确保传播继续进行,即使由于干预而进行的其他操作
CANCELLED 1 由于超时或中断,该节点被取消。 节点永远不会离开此状态。特别是具有取消节点的线程永远不会再次阻塞
  0 表示当前节点在sync队列中,等待着获取锁
  • Node prev:链接到当前节点/线程用来检查waitStatus的先前节点。在入队期间分配,并且仅在出队时将其清空(出于GC的考虑)。同样,在取消前任后,我们会短路,同时找到一个未取消的前任,这将始终存在,因为根节点永远不会被取消:只有成功获取后,结点才变为根。取消的线程永远不会成功获取,并且线程只会取消自身,不会取消任何其他节点
  • Node next:链接到后继节点,当前节点/线程在释放时将其解散。在排队过程中分配,在绕过已取消的前辈时进行调整,在出队时清零(出于GC的考虑)。 enq操作直到附加后才分配前任的下一个字段,因此看到空的下一个字段并不一定意味着该节点位于队列末尾。但是,如果下一个字段显示为空,则我们可以从尾部扫描上一个以进行再次检查。被取消节点的下一个字段设置为指向节点本身而不是null,以使isOnSyncQueue的工作更轻松
  •  Thread thread:使该节点排队的线程。在构造时初始化,使用后置为空。
  •  Node nextWaiter:链接到等待条件的下一个节点,或者链接到特殊值SHARED。由于条件队列仅在以独占模式保存时才被访问,因此我们只需要一个简单的链接队列即可在节点等待条件时保存节点。然后将它们转移到队列中以重新获取。并且由于条件只能是互斥的,因此我们使用特殊值来表示共享模式来保存字段。

猜你喜欢

转载自blog.csdn.net/sinat_33472737/article/details/105645486