Wait queue node class.
等待队列节点类
The wait queue is a variant of a "CLH" (Craig, Landin, and Hagersten) lock queue.
等待队列是“CLH”(Craig、Landin和Hagersten)锁队列的变体。
CLH locks are normally used for spinlocks.
CLH锁通常用于自旋锁。
We instead use them for blocking synchronizers,
我们用它们来阻塞同步器,
but use the same basic tactic of holding some of the control information about a thread in the predecessor of its node.
但是使用相同的基本策略,即在其节点的前身中保存关于线程的一些控制信息。
A "status" field in each node keeps track of whether a thread should block.
每个节点中的“status”字段记录一个线程是否应该阻塞。
A node is signalled when its predecessor releases.
当它的前任释放时,一个节点收到信号。
Each node of the queue otherwise serves as a specific-notification-style monitor holding a single waiting thread.
否则,队列的每个节点都充当一个特定通知样式的监视器,它持有一个等待线程。
The status field does NOT control whether threads are granted locks etc though.
但是status字段不控制线程是否被授予锁等等。
A thread may try to acquire if it is first in the queue.
如果一个线程在队列中处于第一个位置,它可能会尝试获取。
But being first does not guarantee success;
但是做第一个并不能保证成功
it only gives the right to contend.
它只给予竞争的权利。
So the currently released contender thread may need to rewait.
因此,当前发布的竞争者线程可能需要重新等待。
To enqueue into a CLH lock, you atomically splice it in as new tail. To dequeue, you just set the head field.
要将其编入CLH锁,您需要将其作为新尾部自动拼接。要退出队列,只需设置head字段。
<pre>
+------+ prev +-----+ +-----+
head | | <---- | | <---- | | tail
+------+ +-----+ +-----+
</pre>
Insertion into a CLH queue requires only a single atomic operation on "tail",
插入一个CLH队列只需要对“tail”进行一个原子操作,
so there is a simple atomic point of demarcation from unqueued to queued.
所以有一个简单的原子点来区分从未排队到排队。
Similarly, dequeuing involves only updating the "head".
类似地,退出队列只涉及更新“头”。
However, it takes a bit more work for nodes to determine who their successors are,
然而,对于节点来说,确定谁是它们的继任者需要更多的工作
in part to deal with possible cancellation due to timeouts and interrupts.
部分是为了处理由于超时和中断可能导致的取消。
The "prev" links (not used in original CLH locks), are mainly needed to handle cancellation.
“prev”链接(在原来的CLH锁中没有使用),主要用于处理取消。
If a node is cancelled, its successor is (normally) relinked to a non-cancelled predecessor.
如果一个节点被取消,它的后继节点(通常)会被重新连接到一个未取消的前任节点。
For explanation of similar mechanics in the case of spin locks, see the papers by Scott and Scherer at
关于自旋锁的类似机制的解释,请参阅Scott和Scherer的论文
http://www.cs.rochester.edu/u/scott/synchronization/
We also use "next" links to implement blocking mechanics.
我们还使用“next”链接去执行阻塞机制。
The thread id for each node is kept in its own node,
每个节点的线程id保存在自己的节点中,
so a predecessor signals the next node to wake up by traversing next link to determine which thread it is.
因此,一个前任节点通过遍历下一个链接来确定它是哪个线程,从而向下一个节点发出唤醒信号。
Determination of successor must avoid races with newly queued nodes to set the "next" fields of their predecessors.
确定后继者必须避免与新排队节点竞争,以设置其前一个节点的“下一个”字段。
This is solved when necessary by checking backwards from the atomically updated "tail" when a node's successor appears to be null.
当节点的后继者似乎为空时,可以通过从原子更新的“尾部”向后检查来解决这个问题。
(Or, said differently, the next-links are an optimization so that we don't usually need a backward scan.)
或者,换句话说,“下一个链接”是一种优化,因此我们通常不需要向后扫描
Cancellation introduces some conservatism to the basic algorithms.
对消为基本算法引入了一些保守性。
Since we must poll for cancellation of other nodes, we can miss noticing whether a cancelled node is ahead or behind us.
因为我们必须轮询取消其他节点,所以我们可能会忽略取消节点是在我们前面还是后面。
This is dealt with by always unparking successors upon cancellation, allowing them to stabilize on a new predecessor,
解决这个问题的办法是取消后总是取消后继者的停车,让他们稳定在一个新的前任上
unless we can identify an uncancelled predecessor who will carry this responsibility.
除非我们能找出一个未取消的前任谁将承担这项责任。
CLH queues need a dummy header node to get started.
CLH队列需要一个虚拟头节点才能启动。
But we don't create them on construction, because it would be wasted effort if there is never contention.
但我们不会在构建时创建它们,因为如果没有竞争,就会浪费精力。
Instead, the node is constructed and head and tail pointers are set upon first contention.
相反,构造节点并在第一次争用时设置头和尾指针。
Threads waiting on Conditions use the same nodes, but use an additional link.
等待条件的线程使用相同的节点,但使用额外的链接。
Conditions only need to link nodes in simple (non-concurrent) linked queues because they are only accessed when exclusively held.
条件只需要链接简单(非并发)链接队列中的节点,因为它们只在独占状态下才被访问。
Upon await, a node is inserted into a condition queue.
在等待上,将节点插入条件队列。
Upon signal, the node is transferred to the main queue.
在信号上,节点被传输到主队列。
A special value of status field is used to mark which queue a node is on.
status字段的特殊值用于标记节点所在的队列。
Thanks go to Dave Dice, Mark Moir, Victor Luchangco, Bill Scherer and Michael Scott,
感谢Dave Dice、Mark Moir、Victor Luchangco、Bill Scherer和Michael Scott
along with members of JSR-166 expert group, for helpful ideas, discussions, and critiques on the design of this class.
与JSR-166专家组的成员一起,为这个类的设计提供有帮助的想法、讨论和评论。