Java Collection/Executor LinkedTransferQueue 源码

前言


 相关系列

  • 《Java & Collection & 目录》
  • 《Java & Executor & 目录》
  • 《Java & Collection/Executor & LinkedTransferQueue & 源码》
  • 《Java & Collection/Executor & LinkedTransferQueue & 总结》
  • 《Java & Collection/Executor & LinkedTransferQueue & 问题》
     

 涉及内容

  • 《Java & Collection & 总结》
  • 《Java & Collection & Queue & 总结》
  • 《Java & Collection/Executor & BlockingQueue & 总结》
  • 《Java & Collection/Executor & SynchronousQueue & 总结》
  • 《Java & Collection/Executor & TransferQueue & 总结》
  • 《Java & Executor & 总结》
     
     

源码


/** ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.*********************//******* Written by Doug Lea with assistance from members of JCP JSR-166* Expert Group and released to the public domain, as explained at* http://creativecommons.org/publicdomain/zero/1.0/*/package juc;import juc.locks.LockSupport;import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.Consumer;/*** An unbounded {@link TransferQueue} based on linked nodes. This queue orders elements FIFO (first-in-first-out) with respect* to any given producer.  The <em>head</em> of the queue is that element that has been on the queue the longest time for some* producer.  The <em>tail</em> of the queue is that element that has been on the queue the shortest time for some producer.* 一个基于链接节点的无界队列。该队列对任意指定的生产者的元素进行先入先出的排序(标准的队列排序)。队列的头是某个生* 产者存在于队列中最久的元素。队列的尾是某个生产者存在于队列中最短的元素。* <p>* Beware that, unlike in most collections, the {@code size} method is <em>NOT</em> a constant-time operation. Because of the* asynchronous nature of these queues, determining the current number of elements requires a traversal of the elements, and so* may report inaccurate results if this collection is modified during traversal. Additionally, the bulk operations {@code addAll},* {@code removeAll}, {@code retainAll}, {@code containsAll}, {@code equals}, and {@code toArray} are <em>not</em> guaranteed* to be performed atomically. For example, an iterator operating concurrently with an {@code addAll} operation might view only* some of the added elements.* 当心:不像大部分的集,size()方法不是一个常量时间操作(即时间复杂度不为不是O(1))。因为这些队列的异步特点,确定元* 素的数量需要一次元素的遍历,并且如果集在遍历期间被修改可能柏高不精确的结果。此外,主要操作addAll(),removeAll(),* retainAll(),containsAll(),equals()以及toArray()方法无法保证原子性的执行(批量操作本身也不要求原子性吧...)。例如,* 一个迭代器操作与addAll()操作并发可能只看到部分新增的元素(可知迭代器是弱一致性的)。* <p>* This class and its iterator implement all of the <em>optional</em> methods of the {@link Collection} and {@link Iterator} interfaces.* 该类及它的迭代器实现了集接口与迭代器接口的所有可选方法。* <p>* Memory consistency effects: As with other concurrent collections, actions in a thread prior to placing an object into a* {@code LinkedTransferQueue} <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a> actions subsequent* to the access or removal of that element from the {@code LinkedTransferQueue} in another thread.* 内存一致性影响:正如其它并发集,线程中在存放对象至链接迁移队列之前的活动先行发生于其它线程中随后访问或移除链接迁* 移队列中元素的活动。* <p>* This class is a member of the <a href="{@docRoot}/../technotes/guides/collections/index.html"> Java Collections Framework</a>.* 该类是Java集框架的成员。** @param <E> the type of elements held in this collection 集中持有元素的类型* @author Doug Lea* @Description: 链接迁移队列类* @since 1.7*/
public class LinkedTransferQueue<E> extends AbstractQueue<E> implements TransferQueue<E>, Serializable {private static final long serialVersionUID = -3223113410248163686L;/** *** Overview of Dual Queues with Slack **** *** 懈怠双队列概述 **** Dual Queues, introduced by Scherer and Scott (http://www.cs.rice.edu/~wns1/papers/2004-DISC-DDS.pdf) are (linked)* queues in which nodes may represent either data or requests.  When a thread tries to enqueue a data node, but encounters a* request node, it instead "matches" and removes it; and vice versa for enqueuing requests. Blocking Dual Queues arrange that* threads enqueuing unmatched requests block until other threads provide the match. Dual Synchronous Queues (see Scherer,* Lea, & Scott http://www.cs.rochester.edu/u/scott/papers/2009_Scherer_CACM_SSQ.pdf) additionally arrange that* threads enqueuing unmatched data also block.  Dual Transfer Queues support all of these modes, as  dictated by callers.* 双队列,引进于Scherer and Scott (http://www.cs.rice.edu/~wns1/papers/2004-DISC-DDS.pdf)是(链接)队列,其节点* 要么代表数据(插入/放置),要么代表请求(移除/拿取)。当一个线程尝试入队一个数据节点,但遭遇一个请求节点(即队* 列中存在一个请求节点),它转而"匹配"并移除它(即数据节点与请求节点进行互补/匹配并将之从队列中移除);入队请求节* 点与之相同。阻塞中的双队列统筹线程入队中未匹配的请求阻塞(即在入队时没有匹配到匹配节点的请求会被阻塞)直至其它* 线程提供匹配。双同步队列(see Scherer, Lea, & Scott http://www.cs.rochester.edu/u/scott/papers/2009_Scherer_CACM_SSQ.pdf)* 额外管理线程入队中不匹配的数据阻塞。根据调用者的要求,双迁移队列支持所有这些模式。* A FIFO dual queue may be implemented using a variation of the Michael & Scott (M&S) lock-free queue algorithm* (http://www.cs.rochester.edu/u/scott/papers/1996_PODC_queues.pdf). It maintains two pointer fields, "head", pointing to* a (matched) node that in turn points to the first actual (unmatched) queue node (or null if empty); and "tail" that points to the* last node on the queue (or again null if empty). For example, here is a possible queue with four data elements:* 一个先入先出的双队列可能使用Michael & Scott (M&S)的锁自由队列算法实现。它维持两个指针段,"头",指向一个(已匹配* 的)节点,该节点又转而指向首个真实队列节点(如果为空则为null);"尾"指向队列的最后一个节点(如果为空则为空)(事* 实上,尾节点未必是队列的最后一个节点)。例如:这是一个可能的四数据元素队列:** (http://www.cs.rochester.edu/u/scott/papers/1996_PODC_queues.pdf)*  head                tail*    |                   |*    v                   v*    M -> U -> U -> U -> U** The M&S queue algorithm is known to be prone to scalability and overhead limitations when maintaining (via CAS) these head* and tail pointers. This has led to the development of contention-reducing variants such as elimination arrays  and optimistic back* pointers .However, the nature of dual queues enables a simpler tactic for improving M&S-style implementations when dual-ness* is needed.* 众所周知,当M&S队列算法维护(通过CAS)它的头和尾指针时容易受到伸缩性和开销的限制。这导致了减少争用的变体(即* M&S队列算法的改善算法)的发展,如消除数组(see Moir et al http://portal.acm.org/citation.cfm?id=1074013)和乐观返回指针* (see Ladan-Mozes & Shavit http://people.csail.mit.edu/edya/publications/OptimisticFIFOQueue-journal.pdf)。然而,双* 队列的性质能使用一种更简单的策略使得在需要双特性时改进m&s风格的实现。* In a dual queue, each node must atomically maintain its match status. While there are other possible variants, we implement this* here as: for a data-mode node, matching entails CASing an "item" field from a non-null data value to null upon match, and* vice-versa for request nodes, CASing from null to a data value. (Note that the linearization properties of this style of queue are* easy to verify -- elements are made available by linking, and unavailable by matching.) Compared to plain M&S queues, this* property of dual queues requires one additional successful atomic operation per enq/deq pair. But it also enables lower cost* variants of queue maintenance mechanics. (A variation of this idea applies even for non-dual queues that support deletion of* interior elements, such as j.u.c.ConcurrentLinkedQueue.)* 在一个双队列中,每个节点必须原子性地维持它的匹配状态。当有其它可能的变体,我们按这样实现:对于一个数据模式节点,* 匹配需要在匹配后将"项"字段从一个非null数据值(通过CAS)变为null,对于请求节点也是一样,"项"字段从一个null(通过CAS)* 变为一个数据值。(注意:队列这些风格的线性特性很容易验证 —— 元素通过链接变得可用,并通过匹配变的不可用。)相比* 普通的M&S队列,这个双队列的特性对每个入队/出队对需要一次额外成功的原子操作。但是它也能够减少队列维护机制的变体。* (该方案的变化甚至被应用于非双队列以支持内部元素的删除,例如并发链接队列。)* Once a node is matched, its match status can never again change.  We may thus arrange that the linked list of them contain a prefix* of zero or more matched nodes, followed by a suffix of zero or more unmatched nodes. (Note that we allow both the prefix and* suffix to be zero length, which in turn means that we do not use a dummy header.)  If we were not concerned with either time or* space efficiency, we could correctly perform enqueue and dequeue operations by traversing from a pointer to the initial node;* CASing the item of the first unmatched node on match and CASing the next field of the trailing node on appends. (Plus some* special-casing when initially empty).  While this would be a terrible idea in itself, it does have the benefit of not requiring any atomic* updates on head/tail fields.* 曾经一个节点已经互补/匹配,则它的匹配状态永远不会再改变。我们可能因此安排它们的链接链表包含0或更多的已匹配节点的* 前缀(即已匹配的节点不会立即被移除,而会继续保存在队列中),接着是0或更多的未匹配节点的后缀(即在尾节点之后实际上* 还存在追加的未匹配节点,从此处可知尾节点和最后节点不是一个概念,但头节点和第一节点是一个概念)。(注意我们允许前* 缀和后缀都是0长度,这意味着我们无法使用哨兵节点。)如果我们不关心时间和空间效能,我们可以通过一个指针遍历到初始节* 点来正确地执行入队和出队操作(即通过一个指针来从头遍历整个队列来进行出/入队操作);CAS首个未匹配节点的项为匹配以* 及CAS追加踪迹节点的next(下个)字段。(外加当初始为空时一些特殊的CAS)。虽然这本身是一个糟糕的想法,但它确实有一* 个好处,即不需要对头部/尾部字段进行任何原子更新(这里说无需更新头/尾节点的原因是因为首个未匹配节点/最后节点是通过* 指针从初始节点向后遍历定位的,所以头/尾节点会一直保持为初始节点而无需更新...可难道互补/匹配后不用移除匹配节点的?可* 能是只移除后续节点而一直保持初始节点不变吧。)。* We introduce here an approach that lies between the extremes of never versus always updating queue (head and tail) pointers.* This offers a tradeoff between sometimes requiring extra traversal steps to locate the first and/or last unmatched nodes, versus* the reduced overhead and contention of fewer updates to queue pointers. For example, a possible snapshot of a queue is:* 我们在这里介绍一种介于从不更新(上文提及的做法,即用一个指针遍历队列来进行操作)和总是更新队列(头和尾)指针的极端* 之间的方法。这提供一个介于"有时需要额外的遍历步数来确定首个以及/或最后未匹配节点"与"减少开销与更少的竞争更新队列指针"* 之间的折中(即单纯的使用从不更新的方案性能上会很糟糕,而使用经常更新的方案开销上会很严重,因此使用两者的综合方案来* 平衡性能与开销,但是这复杂度嘛...)。**  head           tail*    |              |*    v              v*    M -> M -> U -> U -> U -> U** The best value for this "slack" (the targeted maximum distance between the value of "head" and the first unmatched node, and similarly* for "tail") is an empirical matter. We have found that using very small constants in the range of 1-3 work best over a range of platforms.* Larger values introduce increasing costs of cache misses and risks of long traversal chains, while smaller values* increase CAS contention and overhead.* "懈怠"(即队列头节点与首个未匹配节点之间保留多少个已匹配的节点,队列尾节点后存在多少个未匹配节点)的最佳值(目标最大* 限度距离介于头值与首个未匹配节点之间,并且尾部也相似)是一个经验主义的东西(就是凭感觉...说的这么邪乎)。我们发现,在* 一系列平台上,使用1-3范围内非常小的常数效果最好(队头保留1 - 3个已匹配节点,队尾追加1 - 3个未匹配节点)。巨大的值会增加* 缓存不命中(不知道是什么东西)的成本以及长遍历链(即遍历的节点太多)的风险,而更小的值增加CAS的竞争和开销。* Dual queues with slack differ from plain M&S dual queues by virtue of only sometimes updating head or tail pointers when matching,* appending, or even traversing nodes; in order to maintain a targeted slack.  The idea of "sometimes" may be operationalized in* several ways. The simplest is to use a per-operation counter incremented on each traversal step, and to try (via CAS) to update the* associated queue pointer whenever the count exceeds a threshold. Another, that requires more overhead, is to use random number* generators to update with a given probability per traversal step.* 懈怠双队列不同于普通的M&S双队列,其优点在于只在匹配、追加或遍历节点时才偶尔更新头或尾指针;为了维护一个目标懈怠(* 即通过更新头/尾指针使实际保留/追加的未/已匹配节点维持在指定数量以下)。"有时”这个想法可以通过几种方式来实现。最简单* 的是使用预操作在每次遍历步伐中递增的统计,并每当总数超过一个阈值尝试(通过CAS)更新关联的队列指针(即头/尾字段)(* 即在遍历过程中进行计数,当遍历到目标节点时判断计数值是否达到目标懈怠)。另一种需要更多开销的方法是使用随机数生成器* 以每个遍历步骤的给定概率进行更新(即每遍历一个节点生成一个数字,然后根据生成的数字判断是否更新)。* In any strategy along these lines, because CASes updating fields may fail, the actual slack may exceed targeted slack. However, they* may be retried at any time to maintain targets. Even when using very small slack values, this approach works well for dual queues because* it allows all operations up to the point of matching or appending an item (hence potentially allowing progress by another thread) to* be read-only, thus not introducing any further contention. As described below, we implement this by performing slack maintenance* retries only after these points.* 在这些方面的任何策略中,因为CAS更新字段可能失败,真实的懈怠可能超过目标懈怠(即保留/追加的未/已匹配节点的数量可能* 大于指定数量)。此外它们可能在任何时间重试以维护目的【即更新头/尾指针失败后重试以保持实际的懈怠小于目标懈怠,由于并* 发的原因,该操作可能发生在任何时间】,甚至当使用非常小的懈怠值时,该方法对于双队列也有效 (本意应该是也会发生这种情* 况),因为匹配或追加项之前的所有操作都是只读的【即匹配/追加操作在CAS匹配/追加之前不存在任何其它写操作】(因此可能* 允许另一个线程的进程【即允许并发】),所以不引入任何进一步的争用【即在CAS匹配/追加成功之前只会有匹配/追加的争用】。* 根据下面的描述,我们通过只在这些点之后执行懈怠维护重试来实现这点。* As an accompaniment to such techniques, traversal overhead can be further reduced without increasing contention of head pointer* updates: Threads may sometimes shortcut the "next" link path from the current "head" node to be closer to the currently known first* unmatched node, and similarly for tail. Again, this may be triggered with using thresholds or randomization.* 作为这样技术的搭配,遍历开销能够进一步的减少,并不会增加头指针更新的争用:线程有时可能从当前"头"节点快捷至"next"链接* 路径,使其更接近当前已知的第一个未匹配的节点,尾节点也是如此【即一次跳跃多个节点,而非顺序的向后遍历】。同样,这可能* 使用阈值或随机化来触发。* These ideas must be further extended to avoid unbounded amounts of costly-to-reclaim garbage caused by the sequential "next"* links of nodes starting at old forgotten head nodes: As first described in detail by Boehm* (http://portal.acm.org/citation.cfm?doid=503272.503282) if a GC delays noticing that any arbitrarily old node has become garbage,*  all newer dead nodes will also be unreclaimed. (Similar issues arise in non-GC environments.)  To cope with this in our implementation,* upon CASing to advance the head pointer, we set the "next" link of the previous head to point only to itself; thus limiting the length of* connected dead lists. (We also take similar care to wipe out possibly garbage retaining values held in other Node fields.)  However,* doing so adds some further complexity to traversal: If any "next" pointer links to itself, it indicates that the current thread has lagged* behind a head-update, and so the traversal must continue from the "head".  Traversals trying to find the current tail starting from "tail"* may also encounter self-links, in which case they also continue at "head".* 这些想法必须进一步拓展,以避免连续的next(下个)字段链接的节点从旧被遗忘的头节点开始「遍历」导致无限制昂贵的回收垃圾* 数量【CAS匹配后可能会更新头节点,那头节点之前的节点就相当于被移除/拿取了,但是其依然持有着对队列节点的引用链,故可* 称为外链,队列即内链。若外链中部分节点由于存活时间长而进入老年代,则会导致跨带引用问题的存在,其会使得外链的回收非常* 昂贵【即成本大】。再由于外链频繁的产生,GC消耗会非常严重,因此必须要进行优化。】:Boehm首先详细描述了这一点,如果* GC延迟注意到任意已成为垃圾的旧节点,所有更新的死亡节点将同样是未回收的【即如果GC延迟发现了外链中的某个垃圾节点,则* 整个外链都是未被回收的】(相似的问题出现在无GC的环境中。)为了在我们的实现中处理这种情况,在CAS前进【即更新】头指* 针之后,我们设置先前头节点的next(下个)字段只指向它自己(即自引用);这样限制连接死亡链表的长度【即外链的长度。由于* 懈怠机制,更新头节点的时会一次性前进至少两个节点,意味着一次移除/拿取会形成一条至少两个节点的外链(内链中不存在其它* 节点的特殊情况除外)。而在这其中只有旧头节点会被设置为自引用,而剩余的节点会依然对内链节点保持可达。这就是说,此次外* 链的头节点是上次外链的尾节点】。(我们同样相似地小心消灭在其它节点字段中持有的可能的垃圾保留值【即会使用自引用的方式* 处理大多数的垃圾回收昂贵的问题】。)但是,这么做会添加遍历更多的复杂度:如果任意next(下个)字段指针链接至它自己,它* 表示当前线程落后于头节点更新,所以该遍历必须从头节点继续【即如果某线程在遍历过程中发现某个节点是自引用的,说明在该节* 点已经真正意义上的出队了,遍历需要跳转至头节点重新开始遍历】。遍历尝试从尾节点开始查找到的当前尾节点可能同样遭遇自链* 接【即遍历尾节点之后追加的那些节点也可能应为并发而遭遇自引用的情况,之前已经说过,因为懈怠机制,尾节点和最后节点不是* 一个概念】,在这种情况下它们同样在头节点继续。* It is tempting in slack-based scheme to not even use CAS for updates (similarly to Ladan-Mozes & Shavit). However, this cannot be* done for head updates under the above link-forgetting mechanics because an update may leave head at a detached node. And while* direct writes are possible for tail updates, they increase the risk of long retraversals, and hence long garbage  chains, which can be* much more costly than is worthwhile considering that the cost difference of performing a CAS vs write is smaller when they are not* triggered on each operation(especially considering that writes and CASes equally require additional GC bookkeeping ("write barriers")* that are sometimes more costly than the writes themselves because of contention).* 在基于懈怠的方案中甚至不使用CAS进行更新是很诱人的(类似于Ladan-Mozes & Shavit)。然而,这无法在上文提及的链接遗忘* 机制下完成头节点的更新,因为一次更新可能在一个分离节点留下头部【即头节点指向一个外链中的节点。由于并发的原因,可能有* 多个匹配操作同时执行。如果它们倚仗的头节点(快照)相同,并且都各自互补/匹配了匹配节点,则如果不是用CAS来更新头节点,* 那所有的线程更新头节点都能够成功,这就无法保证头节点的值是不可预测的,其可能是任意线程所匹配节点的后继节点,即其可能* 指向一个已移除/拿取的节点】。虽然直接写入【即不使用CAS】尾节点更新是可能的,但它们增加长重遍历的风险【与不通过CAS* 更新头节点原因相同,尾节点可能指向任意线程追加的节点,而这个节点位于通过CAS得到的尾节点的前方。如此,后续线程追加节* 点时为了找到真正的最后节点,就需要以该尾节点为起点遍历更多的节点】,并因此导致长垃圾链【垃圾连应该指的是外链,但为什* 么会导致更长的外链...目前为止我完全没有想出相应的场景】,如果每个操作都没有触发CAS,那么执行CAS和执行写操作的成本差* 异就会更小(特别是考虑到写操作和CAS操作同样需要额外的GC簿记(“写障碍”),由于争用,这些簿记有时比写操作本身的成本* 还要高)【理解的不是很清楚,大概是说CAS操作和单纯的写操作相比,成本不会差异的太大】。** *** Overview of implementation **** *** 实现概述 **** We use a threshold-based approach to updates, with a slack threshold of two -- that is, we update head/tail when the current pointer* appears to be two or more steps away from the first/last node. The slack value is hard-wired: a path greater than one is naturally* implemented by checking equality of traversal pointers except when the list has only one element, in which case we keep slack threshold* at one. Avoiding tracking explicit counts across method calls slightly simplifies an already-messy implementation. Using randomization* would probably work better if there were a low-quality dirt-cheap per-thread one available, but even ThreadLocalRandom is too heavy* for these purposes.* 我们使用基于阈值的方法进行更新,有两个懈怠阈值 —— 即,当当前指针距离头/最后节点两个或更多步伐时我们更新头/尾节点。* 懈怠值是固有的:大于1的路径自然是通过检查遍历指针的相等性来实现的【即通过指针找到首个未匹配节点/最后节点后,如果匹配/* 追加前后的首个未匹配节点/最后节点相等,说明没有别的线程执行匹配/追加操作,否则说明存在并发】,除非列表【即队列】只存* 在一个元素,在这种情况下我们保持懈怠阈值为1【这是一种特殊情况,实际上阈值依然是2,但是由于队列中只有一个未匹配的节点,* 因此在匹配后队列中只剩下两个已匹配节点。按正常的流程,两个已匹配节点刚好会因为懈怠阈值而触发移除/拿取,但由于后续需* 要追加节点的原因,其中一个节点会被强制的遗留,所以说在这种情况下懈怠阈值为1,但实际上是在懈怠阈值2的基础上执行一步特* 殊处理】。避免跨方法调用跟踪显式计数略微简化了本已混乱的实现【个人理解"跨方法调用跟踪显式计数"是指通过一个变量记录具* 体的懈怠值,并将该变量在方法之间传递】。如果有一种低质量、低成本的线程随机化方法可用,那么使用随机化可能会更好【即使* 用随机化的方式来触发懈怠阈值】,但对于这些目的来说,即使是ThreadLocalRandom也太沉重【重量级】了。* With such a small slack threshold value, it is not worthwhile to augment this with path short-circuiting (i.e., unsplicing interior nodes)* except in the case of cancellation/removal (see below).* 对于如此小的懈怠阈值,不值得用路径短路来增加它(例如:断开内部节点的拼接),除了取消/移除【特指内部移除/迭代移除】的* 情况(看下文)。* We allow both the head and tail fields to be null before any nodes are enqueued; initializing upon first append. This simplifies some other* logic, as well as providing more efficient explicit control paths instead of letting JVMs insert implicit NullPointerExceptions when they are* null. While not currently fully implemented, we also leave open the possibility of re-nulling these fields when empty (which is complicated* to arrange, for little benefit.)* 我们允许头和尾字段在任意节点入队前都为null;在首次追加后初始化。这简化了一些其它逻辑,而且当它们为null时提供更多有效的* 明确的控制路径代替令JVM插入隐式的空指针异常【即提供了明确的空判断,而非只能在运行中遭遇null时抛出空异常】。虽然目前还* 没有完全实现,但我们也保留了在这些字段为空时重新为空的可能性(安排起来很复杂,也没什么好处。)【即代码中很多的空判断* 都是为以后准备的,就目前的实现来说当有节点被追加之后头/尾节点就不可能再次为null...我说怎么源码看的一头问号呢...】。* All enqueue/dequeue operations are handled by the single method "xfer" with parameters indicating whether to act as some form of offer,* put, poll, take, or transfer (each possibly with timeout). The relative complexity of using one monolithic method outweighs the code bulk* and maintenance problems of using separate methods for each case.* 所有入队/出队操作通过单例方法"xfer"进行处理,关于参数表明是否作为提供,放置,轮询,拿取或迁移中的某个形式(每种都可能* 超时)【即根据传入参数的不同,执行上述的不同操作的不同形式的方法】。对于各种情况,使用整体方法的相对复杂度比使用分离* 方法的代码量和维护项目更加重要【即基于复杂度上的考虑,使用了一个整体的方法而不是各自分离的实现】。* Operation consists of up to three phases. The first is implemented within method xfer, the second in tryAppend, and the third in method* awaitMatch.* 操作由三个阶段组成。第一个阶段由xfer()方法实现,第二阶段在tryAppend()方法中,以及第三阶段在awaitMatch()方法中。* 1. Try to match an existing node* 1. 尝试匹配一个存在的节点* Starting at head, skip already-matched nodes until finding an unmatched node of opposite mode, if one exists, in which case matching* it and returning, also if necessary updating head to one past the matched node (or the node itself if the list has no other unmatched* nodes). If the CAS misses, then a loop retries advancing head by two steps until either success or the slack is at most two. By requiring* that each attempt advances head by two (if applicable), we ensure that the slack does not grow without bound. Traversals also check* if the initial head is now off-list, in which case they  start at the new head.* 从头节点开始,跳过已经匹配的节点直到找到一个相反模式的未匹配节点,如果存在,在这种情况下匹配它并返回,如果必要则更新* 头节点至匹配节点的后继节点(或者如果列表没有其它的未匹配的节点则为节点自身)。如果CAS错过,则循环重试前进头节点两步* 直至要么成功要么最多两步懈怠【即如果更新头节点失败,则说明有其它执行互补/匹配的线程并发更新了头节点。此时为了确保在并* 发的环境下的懈怠值小于2,当前线程会不断前进两个已匹配节点并尝试将之CAS为头节点,直至成功或者懈怠值 < 2为止】。通过要* 求每次尝试向前推进两个头(如果适用),我们确保懈怠不会无限制地增长。遍历还检查初始头是否已从列表中删除,在这种情况下,* 遍历将从新的头开始【即在寻找首个未匹配节点的遍历过程中,如果发现某个节点是自引用的,则说明该节点已物理移除/拿取。这种* 情况是实际存在的,由于互助/匹配操作并发的原因,原本存在于内链的节点很有可能在过程中成为外链【逻辑移除/拿取】,甚至是* 垃圾链【真实移除/拿取】的节点。其中遍历外链时还有可能回到内链的话,而一旦遍历到垃圾链的节点,就不可能在回到内链了,必* 须重新从队列/内链开始遍历】。* If no candidates are found and the call was untimed poll/offer, (argument "how" is NOW) return.* 如果没有找到候补【即可匹配节点】并且调用定时的poll/offer方法,(参数"how"为NOW【意味着只执行互补/匹配阶段】)返回。* 2. Try to append a new node (method tryAppend)* 2. 尝试追加一个新节点(方法tryAppend())* Starting at current tail pointer, find the actual last node and try to append a new node (or if head was null, establish the first node).* Nodes can be appended only if their predecessors are either already matched or are of the same mode. If we detect otherwise, then a* new node with opposite mode must have been appended during traversal, so we must restart at phase 1. The traversal and update steps* are otherwise similar to phase 1: Retrying upon CAS misses and checking for staleness.  In particular, if a self-link is encountered, then* we can safely jump to a node on the list by continuing the traversal at current head.* 从当前尾指针开始,找到真实的最后节点并尝试追加一个新节点(或者如果头为null,建立首个节点)。节点只能在前驱节点【即最后* 节点】要么已匹配,要么模式相同时追加【由此可知队列中是可能存在两种不同模式的节点,只不过在这种情况下,前一种模式的节* 点都是懈怠节点】。如果我们检测到相反的情况【即检测到与追加节点模式相反的未匹配节点】,那么相反模式的新节点必定已在遍* 历期间追加【即有其它线程并发的追加了一个相反模式的节点】,所以我们必须在阶段1重试。「阶段二」遍历和更新步伐在其它方面* 类似于阶段一:在CAS错过【失败】和检查过期之后重试。特别地,如果已遭遇一个自引用,那我们可以在列表上于当前头节点通过* 遍历继续安全地跳跃。* On successful append, if the call was ASYNC, return.* 在成功追加「的基础上」,如果调用是ASYNC【即指执行一二阶段】,返回。* 3. Await match or cancellation (method awaitMatch)* 3.等待匹配或取消(方法awaitMatch())* Wait for another thread to match node; instead cancelling if the current thread was interrupted or the wait timed out. On multiprocessors,* we use front-of-queue spinning: If a node appears to be the first unmatched node in the queue, it spins a bit before blocking. In either* case, before blocking it tries to unsplice any nodes between the current "head" and the first unmatched node.* 等待其它线程匹配节点;相反,如果当前线程被中断或等待超时则取消。在多核处理器上,我们使队列前部「的节点」自旋;如果节* 点是队列中首个未匹配节点,它会在阻塞前自旋。在这两种情况下,在阻塞之前它尝试断开介于当前头节点与首个未匹配节点之间所* 有的节点的拼接【哈?还有这步操作?源码里没看见啊】。* Front-of-queue spinning vastly improves performance of heavily contended queues. And so long as it is relatively brief and "quiet",* spinning does not much impact performance of less-contended queues.  During spins threads check their interrupt status and generate* a thread-local random number to decide to occasionally perform a Thread.yield. While yield has underdefined specs, we assume that it* might help, and will not hurt, in limiting impact of spinning on busy systems. We also use smaller (1/2) spins for nodes that are not known* to be front but whose predecessors have not blocked -- these "chained" spins avoid artifacts of front-of-queue rules which otherwise* lead to alternating nodes spinning vs blocking. Further, front threads that represent phase changes (from data to request node or vice* versa) compared to  their predecessors receive additional chained spins, reflecting longer paths typically required to unblock threads* during phase changes.* 队列前部自旋极大地改善了沉重竞争队列的性能。并且只要它相对短暂和"安静"【即自旋的次数适当】,自旋不会对少竞争队列的性* 能造成太大影响。在自旋期间线程检查它们的中断状态并生成一个线程本地随机数以决定偶尔的执行线程yield()。如果yield()有下定* 义规范,我们假设它可能帮助,并不会在忙碌系统中损害、限制自旋的作用。对于不知道是否是前部节点但前驱节点未阻塞的节点我* 们使用更小的(1/2)自旋 —— 这些"链式"自旋避免了队列前部规则的假象,否则将导致交互的节点自旋和阻塞【意思是节点是否处于* 队列前部的判断规则并不严谨,可能会造成误判,而"链式"自旋的存在可以弥补这种误判...我猜应该是这样】。此外,代表阶段性变化* 的前部线程(从数据到请求节点或与之相反【即两种模式的节点共存于队列中时,首个模式交替的节点】)与它们接受额外链式自旋* 的前驱节点相比,反映出在阶段性变化期间通常需要更长的路径来不阻塞线程【即对于这种代表模式变化的节点,需要更多的自旋次* 数,这是因为这样的节点必定是队列中首个可匹配的节点,应该极力的避免阻塞】。** ** Unlinking removed interior nodes *** ** 断开移除的内部节点的链接 *** In addition to minimizing garbage retention via self-linking described above, we also unlink removed interior nodes. These may arise due* to timed out or interrupted waits, or calls to remove(x) or Iterator.remove.  Normally, given a node that was at one time known to be the* predecessor of some node s that is to be removed, we can unsplice s by CASing the next field of its predecessor if it still points to s (* otherwise s must already have been removed or is now offlist). But there are two situations in which we cannot guarantee to make node* s unreachable in this way: (1) If s is the trailing node of list (i.e., with null next), then it is pinned as the target node for appends, so can* only be removed later after other nodes are appended. (2) We cannot necessarily unlink s given a predecessor node that is matched (* including the case of being cancelled): the predecessor may already be unspliced, in which case some previous reachable node may still* point to s. (For further explanation see Herlihy & Shavit "The Art of Multiprocessor Programming" chapter 9).  Although, in both cases,* we can rule out the need for further action if either s or its predecessor are (or can be made to be) at, or fall off  from, the head of list.* 除通过上文提及的自引用将垃圾保留减少到最小限度外,我们还会断开已移除内部节点的链接。它们【即需内部移除的节点】可能由* 于超时、中断等待或调用remove(x)方法或Iterator.remove()方法产生。通常,指定一个在某个时刻已知要被移除的节点s的前驱节点,* 如果它依然指向节点s【即其后继节点为s。虽然在选中时其确实是节点s的前驱节点,但由于并发可能在断开连接的过程中已经物理移* 除/拿取而不再是节点s的前驱节点了】,我们可以通过CAS操作修改它【即节点s】的前驱节点【即指定的节点】的next(下个)字段* 来断开节点s的链接(否则节点s必须已经「中间」移除或脱离列表【即物理移除/拿取。这里一种比较特殊的情况是由于移除/拿取一* 般会移除至少两个节点,而自引用的只有外链的头节点,其它节点无法判断是否处于内链中,因此内部移除断开链接的很有可能是一* 个外链或者是垃圾链的节点】)。但是用这种方法有两种情况我们无法保证标记节点s是不可达的:(1) 如果节点s在列表【队列】中是* 最后节点(例如,next为null),则其被牵制作为追加的目标节点,因此只能在稍后其它节点追加之后移除【这种情况并不是无法内部* 移除,而是在移除过程中会受到追加操作的并发影响导致新节点可能追加到内部移除的垃圾节点上,因此人为强制的保留】。(2) 我们* 不一定要/能断开节点s指定已匹配(包括已取消的情况)前驱节点「与节点s的」的拼接:前驱节点可能已经断开拼接,在这种情况下* 先前有些可到达的节点可能依然指向s【即前驱节点本身也在执行内部移除,这种情况下前前驱节点就会持有节点s的引用。如此就存在* 前驱节点和前前驱节点两个节点持有了节点s的后继引用,虽然后续会将前期节点指向节点s的后继节点,但由于前前驱节点依然指向节* 点s,因此节点s依然存在于队列】(进一步解释/说明看Herlihy & Shavit "The Art of Multiprocessor Programming" chapter )。但是,* 在这两种情况下,如果节点s或其前驱节点是(或可以是)在列表头,或从列表头脱离,我们就可以排除进一步行动的需要【如果节点s* 被遗留在队列中,那肯定是需要特别处理的,但是有两种情况可以避免,即前驱节点/节点s在进行特别处理前已经实际移除/拿取。这* 两种情况分别说明节点s快被或已被实际移除/拿取,如此一来也就无需特殊处理了】。* Without taking these into account, it would be possible for an unbounded number of supposedly removed nodes to remain reachable.* Situations leading to such buildup are uncommon but can occur in practice; for example when a series of short timed calls to poll* repeatedly time out but never otherwise fall off the list because of an untimed call to take at the front of the queue.* 如果不考虑这些因素,可能会有无限数量的假定已移除的节点保持可达【即会有累积很多本该内部移除就又被遗留的就饿点】。局面导* 向如此发展是不普通【常见】的,但是会在实践中发生;例如当一系列短定时调用poll()方法重复超时【重复超时...应该说的批量超时】* 但因为一个非定时调用take()方法在队列的前端所以永远无法以其它方式从列表中脱离【因为take()方法是无限阻塞的,因此如果没有* 插入/放置操作来此匹配,会永远存在于队列中。此时对于后继的短定时节点,由于在相近时间大量的失效,并且只能通过断开拼接的* 方式从队列中脱离,就有一定可能产生上述情况】。* When these cases arise, rather than always retraversing the entire list to find an actual predecessor to unlink (which won't help for* case (1) anyway), we record a conservative estimate of possible unsplice failures (in "sweepVotes"). We trigger a full sweep when the* estimate exceeds a threshold ("SWEEP_THRESHOLD") indicating the maximum number of estimate d removal failures to tolerate* before sweeping through, unlinking cancelled nodes that were not unlinked upon initial removal. We perform sweeps by the thread* hitting threshold (rather than background threads or by spreading work to other threads) because in the main contexts in which* removal occurs,  the caller is already timed-out, cancelled, or performing a potentially O(n) operation (e.g. remove(x)), none of which* are time-critical enough to warrant the overhead that alternatives would impose on other threads.* 当这些情况发生时,我们报告一个可能断开拼接失败(在"sweepVotes")的保守估计【即递增一次可能失败的统计】,而不是经常重遍* 历整个列表【队列】以找到一个真正的前驱节点以断开链接(不会对情况(1)有任何帮助【因为情况(1)是被人为强制保留的,如果因为重* 遍历整个队列而移除反而会引发异常】)。当估计值超过阈值("SWEEP_THRESHOLD")【该阈值表示在清理通过(执行)之前估计* 的可容忍的移除失败的最大数量】时我们触发一次完全的清理,断开在内部移除之后未断开链接的已取消节点的链接。我们通过线程触* 达阈值执行清理(而不是后台线程或通过传播工作至其它线程),因为在主上下文中,在任何移除发生的时候,调用者都已经超时,取* 消(准确的说是中断)或者执行潜在的时间复杂度为O(n)的操作(例如remove(x)),它们都不是时间关键型的「操作」【即不要求非* 常快速的返回结果/产生回应】,不足以保证要强制推行的替代方案【遍历整个队列并清理以匹配/取消的节点】在其他线程上的开销【* 即完全清理之所以发生在主线程中而不是发生在一个专属的后台线程中是因为会产生节点遗留的主线程执行的操作都对执行实现要求不* 严格,可以接受完全清理带来的额外时间消耗。至于"不足以保证要强制推行的替代方案在其他线程上的开销"这句话看不懂什么意思,* 可能是开销比较大吧】。* Because the sweepVotes estimate is conservative, and because nodes become unlinked "naturally" as they fall off the head of the queue,* and because we allow votes to accumulate even while sweeps are in progress, there are typically significantly fewer such nodes than* estimated. Choice of a threshold value balances the likelihood of wasted effort and contention, versus providing a worst-case bound on* retention of interior nodes in quiescent queues. The value defined below was chosen empirically to balance these under various timeout* scenarios.* 因为sweepVotes(清理选票)字段的估计是保守的,并且因为节点已断开链接"自然地"从队列的头部脱离,以及因为即使当清理进行时* 我们也允许积累选票,所以如此的节点【即遗留节点】通常显著地少于估计。相比提供一个最差情况限制内部节点在静止队列中的存放,* 阈值的选择更可以平衡已浪费的精力和争用的可能【个人理解是相比使用悲观锁避免节点遗留的产生,使用阈值触发清理的方案在性能* 和成本上更加令人接受】。下面定义的值是根据经验选择的,以在各种超时情况下平衡这些值。* Note that we cannot self-link unlinked interior nodes during sweeps. However, the associated garbage chains terminate when some* successor ultimately falls off the head of the list and is self-linked.* 注意:我们无法在清理期间自引用断开内部节点的链接。此外,当某些后继对象最终从列表头部脱落并自链接时,相关的垃圾链将终止。*//*** True if on multiprocessor* 如果是多核处理器则为true** @Description: 多核处理器:表示当前机器是否是多核处理器,是则为true;否则为false。*/private static final boolean MP = Runtime.getRuntime().availableProcessors() > 1;/*** The number of times to spin (with randomly interspersed calls to Thread.yield) on multiprocessor before blocking when a node is apparently* the first waiter in the queue.  See above for explanation. Must be a power of two. The value is empirically derived -- it works pretty well across* a variety of processors, numbers of CPUs, and OSes.* 当节点表面上是队列中的首个等待者时,阻塞前在多核处理器中自旋的次数(随机的分散调用Thread.yield)。解释/说明看上文。必须是2* 的幂。该值的获取以经验为主 ——它在各类处理器,CPU的数量及操作系统上工作的相当好。** @Description: 前自旋:队列的前部等待者的自旋次数,具体值为128。*/private static final int FRONT_SPINS = 1 << 7;/*** The number of times to spin before blocking when a node is preceded by another node that is apparently spinning.  Also serves as an increment* to FRONT_SPINS on phase changes, and as base average frequency for yielding during spins. Must be a power of two.* 当一个节点前面有另一个明显在旋转的节点时,在阻塞之前旋转的次数。在阶段性变化中作为FRONT_SPINS(前自旋)的增量,并在自旋期* 间作为让步的基础平均频率。必须是2的幂。** @Description: 链接自旋:节点的前驱节点在自旋时的自旋次数,具体值为64。*/private static final int CHAINED_SPINS = FRONT_SPINS >>> 1;/*** The maximum number of estimated removal failures (sweepVotes) to tolerate before sweeping through the queue unlinking cancelled nodes* that were not unlinked upon initial removal. See above for explanation. The value must be at least two to avoid useless sweeps when removing* trailing nodes.* 在清理通过队列断开已取消的,但在内部移除之后未断开链接的节点之前所允许的移除失败(sweepVotes)的估计最大限度数量。解释/说明* 看上文。该值必须最小为2以避免当移除最后节点时无用的清理。** @Description: 清理阈值:触发完全清理的阈值,当清理选票达到该值时会触发完全清理,即遍历整个链接迁移队列进行清理。*/static final int SWEEP_THRESHOLD = 32;/*** Queue nodes. Uses Object, not E, for items to allow forgetting them after use.  Relies heavily on Unsafe mechanics to minimize unnecessary* ordering constraints: Writes that are intrinsically ordered wrt other accesses or CASes use simple relaxed forms.* 队列节点。使用对象,非泛型,对于项允许在使用之后忘记它们。重量级依赖Unsafe机制以使非必要的排序约束最小化:写入是固有顺序的,* 关于其它访问或使用简单自在的格式进行CAS(即写入顺序与线程的访问顺序与CAS的操作顺序有关)。** @Description: 节点类*/static final class Node {private static final long serialVersionUID = -3375979862319811754L;// false if this is a request node// 如果是请求节点则为false/*** @Description: 是否为数据:表示节点是数据(插入/放置、迁移)节点还是请求(移除/拿取)节点。如果是数据节点则为true;否则为false。*/final boolean isData;// initially non-null if isData; CASed to match// 起初如果isData(是否为数据)为true则不为null;通过CAS匹配/*** @Description: 项:持有元素的引用。初始状态下,如果节点是数据(插入/放置、迁移)节点则不为null;否则为null。除此以外,该字段还被* @Description: 用于判断当前节点是否已经取消。如果引用的对象是节点本身则表示已取消;否则表示未取消。*/volatile Object item;/*** @Description: 下个:持有队列中后继节点的引用。如果是尾节点则为null;如果为自身则表示节点从队列中实际移除/拿取。*/volatile Node next;// null until waiting// 指定等待中为null/*** @Description: 等待者:持有节点所在线程的引用。线程阻塞前会将自身引用保存在该字段中,供匹配的节点将自身唤醒。*/volatile Thread waiter;/*** Constructs a new node.  Uses relaxed write because item can only be seen after publication via casNext.* 构造一个新节点。使用轻松的写入(即实时性相对较弱)因为item(项)字段只在通过casNext()方法发布后可见。** @Description: 创建一个指定项及指定模式的节点。*/Node(Object item, boolean isData) {UNSAFE.putObject(this, itemOffset, item); // relaxed writethis.isData = isData;}// CAS methods for fields// 字段的CAS方法/*** @Description: CAS下个:通过CAS操作修改next(下个)字段的值。*/final boolean casNext(Node cmp, Node val) {return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);}/*** @Description: CAS项:通过CAS操作修改item(项)字段的值。*/final boolean casItem(Object cmp, Object val) {// assert cmp == null || cmp.getClass() != Node.class;// 断言cmp == null或者cmp.getClass() != Node.class(表示已取消)。return UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val);}/*** Links node to itself to avoid garbage retention.  Called only after CASing head field, so uses relaxed write.* 链接节点至自身以避免垃圾保留。只能在CAS操作head(头)字段后调用,所以使用轻松的写入。** @Description: 遗忘下个:将节点的后继引用赋值为自身,表示该节点已被实际移除/拿取,帮助节点被GC回收。*/final void forgetNext() {UNSAFE.putObject(this, nextOffset, this);}/*** Sets item to self and waiter to null, to avoid garbage retention after matching or cancelling. Uses relaxed writes because order is already* constrained in the only calling contexts: item is forgotten only after volatile/atomic mechanics that extract items.  Similarly, clearing waiter* follows either CAS or return from park (if ever parked; else we don't care).* 设置项为自身及等待者为null,以避免在匹配或取消之后垃圾保留。使用轻松的写入因为在唯一调用上下文中顺序已经被限制了:项只在* volatile/atomic机制后提取项之后遗忘。类似的,清除等待者要么在CAS,要么在从等待返回(如果一直等待;否则我们不管)后发生。** @Description: 遗忘内容:将节点的项赋值为自身,将节点的等待者赋值为null,帮助节点及节点引用的对象被GC回收。*/final void forgetContents() {UNSAFE.putObject(this, itemOffset, this);UNSAFE.putObject(this, waiterOffset, null);}/*** Returns true if this node has been matched, including the case of artificial matches due to cancellation.* 如果节点已经匹配则返回true,包括由于取消而人为匹配的情况。** @Description: 是否匹配:判断当前节点是否已经匹配/取消。是则返回true;否则返回false。*/final boolean isMatched() {Object x = item;// x == this:表示取消,也是匹配的一种;// ((x == null) == isData):项与模式不匹配,说明已经完成了数据交换,表示已经匹配。return (x == this) || ((x == null) == isData);}/*** Returns true if this is an unmatched request node.* 如果是一个未匹配/取消的请求(移除/拿取)节点则返回true。** @Description: 是否未匹配请求:判断当前节点是否一个未匹配/取消的请求(移除/拿取)节点。是则返回true;否则返回false。*/final boolean isUnmatchedRequest() {// 判断节点是否是请求(移除/拿取)节点;// 判断项是否为null,为null则说明未取消/未匹配。return !isData && item == null;}/*** Returns true if a node with the given mode cannot be appended to this node because this node is unmatched and has opposite data mode.* 如果一个指定模式的节点因为当前节点未匹配/取消且是相反的数据模式而无法追加至当前节点则返回true。** @Description: 不可先于:判断当前节点是否不可作为指定模式节点的前驱节点,条件是两者模式不同且当前节点未被匹配/取消。是则返回* @Description: true;否则返回false。*/final boolean cannotPrecede(boolean haveData) {boolean d = isData;Object x;// 如果追加节点的模式与当前节点不同,并且当前节点未被取消/匹配,则当前节点不允许作为追加节点的前驱节点。但如此说来,如果两// 者虽然模式不同,但是当前节点已被匹配/取消的情况下,则当前节点是允许被作为追加节点的前驱节点的。也就是说,队列中很有可能// 会存在两种不同模式的节点,只不过位于队列前端的那些节点都是被已被匹配/取消的。return d != haveData && (x = item) != this && (x != null) == d;}/*** Tries to artificially match a data node -- used by remove.* 尝试人为的匹配一个数据(插入/放置)节点 —— 通过remove()使用。** @Description: 尝试匹配数据:尝试强制的将一个未匹配/取消的数据(插入/放置)节点设置为已匹配的状态。如果成功则返回true;否则* @Description: 返回false。*/final boolean tryMatchData() {// assert isData;// 断言当前节点是数据(插入/放置)节点。Object x = item;if (x != null && x != this && casItem(x, null)) {LockSupport.unpark(waiter);return true;}return false;}// Unsafe mechanics// 乐观锁机制。private static final sun.misc.Unsafe UNSAFE;private static final long itemOffset;private static final long nextOffset;private static final long waiterOffset;static {try {UNSAFE = sun.misc.Unsafe.getUnsafe();Class<?> k = Node.class;itemOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("item"));nextOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("next"));waiterOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("waiter"));} catch (Exception e) {throw new Error(e);}}}/*** head of the queue; null until first enqueue* 队列的头;在首次入队之前为null** @Description: 头:持有链接迁移队列头节点的引用。*/transient volatile Node head;/*** tail of the queue; null until first append* 队列的尾;在首次入队之前为null** @Description: 尾:持有链接迁移队列尾节点的引用。*/private transient volatile Node tail;/*** The number of apparent failures to unsplice removed nodes* 表面上断开已移除节点拼接的失败次数** @Description: 清理选票:记录断开已取消(超时/中断)节点拼接的可能失败次数。*/private transient volatile int sweepVotes;// CAS methods for fields// 字段的CAS方法/*** @Description: CAS尾:通过CAS操作修改tail(尾)字段的值。*/private boolean casTail(Node cmp, Node val) {return UNSAFE.compareAndSwapObject(this, tailOffset, cmp, val);}/*** @Description: CAS头:通过CAS操作修改head(头)字段的值。*/private boolean casHead(Node cmp, Node val) {return UNSAFE.compareAndSwapObject(this, headOffset, cmp, val);}/*** @Description: CAS清理选票:通过CAS操作修改sweepVotes(清理选票)字段的值。*/private boolean casSweepVotes(int cmp, int val) {return UNSAFE.compareAndSwapInt(this, sweepVotesOffset, cmp, val);}/** Possible values for "how" argument in xfer method.* 在xfer()方法中 "how"参数可能的值*//*** for untimed poll, tryTransfer* 执行非定时poll,,tryTransfer方法** @Description: 现在:表示操作会跳过追加节点与线程自旋/等待两个步骤,只执行互补/匹配步骤。*/private static final int NOW = 0;/*** for offer, put, add* 执行offer, put, add方法** @Description: 异步:表示操作会跳过线程自旋/等待步骤,只执行互补/匹配及追加节点两个步骤。*/private static final int ASYNC = 1;/*** for transfer, take* 执行transfer, take方法** @Description: 同步:表示操作会完整执行互补/匹配、追加节点及线程自旋/等待三个步骤。*/private static final int SYNC = 2;/*** for timed poll, tryTransfer* 执行定时poll, tryTransfer方法** @Description: 定时:表示操作会完整执行互补/匹配、追加节点及线程自旋/等待三个步骤,并在线程自旋/等待步骤中添加了定时逻辑。*/private static final int TIMED = 3;/*** @Description: 投射:将指定项转换为指定泛型。*/@SuppressWarnings("unchecked")static <E> E cast(Object item) {// assert item == null || item.getClass() != Node.class;// 断言item == null或者item.getClass() != Node.class。return (E) item;}/*** Implements all queuing methods. See above for explanation.* 实现所有的队列方法,结束/说明看上文。** @param e        the item or null for take 拿取的项或null* @param haveData true if this is a put, else a take 如果是插入/放置则为true;否则为false* @param how      NOW, ASYNC, SYNC, or TIMED NOW,ASYNC,SYNC或TIMED* @param nanos    timeout in nanosecs, used only if mode is TIMED 超时的纳秒时间,只在模式是定时时使用* @return an item if matched, else e 如果已匹配则返回项,否则返回e* @throws NullPointerException if haveData mode but e is null*                              空指针异常:如果haveData(存在数据)为true但e为null* @Description: 名称:迁移* @Description: 作用:执行互补/匹配步骤并返回匹配节点的的项。如果链接迁移队列中没有可匹配的元素则跳转至后续步骤。* @Description: 逻辑:遍历链接迁移队列,找到首个可匹配节点。将首个可匹配节点与传入的操作进行比对,如果模式不同则进行互补/* @Description: 匹配。互补/匹配的本质是交换两个操作的项。完成后返回匹配节点的项,并在满足至少两个懈怠节点的情况下将已匹配* @Description: 节点从链接迁移队列中移除/拿取(移除/拿取由于并发的原因可能失败,因此需要不断的执行直至成功或懈怠节点数量* @Description: 小于2时停止)。最后在唤醒匹配节点中可能等待的线程;如果模式相同说明无法匹配,需要跳转至后续的追加节点(如* @Description: 果由于并发而失败,则重新执行互补/匹配步骤)和线程自旋/等待步骤(具体是否执行视how(如何)参数值而定)。*/private E xfer(E e, boolean haveData, int how, long nanos) {// 判断参数是否合法。if (haveData && (e == null))throw new NullPointerException();// the node to append, if needed// 如果需要,该节点用于追加Node s = null;retry:// restart on append race// 当追加竞争时重试// 无限循环,直至完成指定目标。for (; ; ) {// find & match first node// 找到并匹配首个节点。// 从头节点开始向后遍历,以查找可匹配的队列节点。for (Node h = head, p = h; p != null; ) {boolean isData = p.isData;Object item = p.item;// unmatched// 未匹配// 如果队列节点未匹配/取消,尝试进行匹配,否则继续向后遍历。if (item != p && (item != null) == isData) {// can't match// 无法匹配// 如果操作与队列节点的模式相同,则说明当前操作无法进行匹配,直接结束当前循环,尝试将操作封装为节点加入队列中。if (isData == haveData)break;// match// 匹配// 将操作的元素替换队列节点的元素,即所谓的匹配,即令双方都获取到双方所需的数据。如果CAS操作成功,说明匹配成功,进行// 头节点的出队操作;否则说明头节点已取消或已被其它线程的节点匹配,循环重试。if (p.casItem(item, e)) {// 对已匹配/取消的节点进行清除。循环的条件是q != h,因此可知当队列中只有一个已匹配的节点时是不会进行清除的,这就是所谓// 的懈怠,即在匹配/取消的节点达到一定量之前是不会进行清除的。for (Node q = p; q != h; ) {// update by 2 unless singleton// 通过2更新,除非单例。// 尝试将匹配节点以及其所有前驱节点一次性从队列中全部移除/拿取(如果该匹配节点是队列的最后节点,则会将匹配节点保留// 在队列中,因此除了初始情况,头节点永远都不可能为null),如果成功,则结束此次循环,唤醒匹配节点所在的线程。如果失// 败了,说明已经有其它线程完成了这一步。因此可知,头部的移除是一次性移除多个节点的。Node n = q.next;if (head == h && casHead(h, n == null ? q : n)) {// 将已移除/拿取的头节点设置为自引用,帮助GC回收。由于队列的头部出队是一次性至少移除两个节点,而遗忘只遗忘头节点// 的数据,因此可知,在下个头节点被出队并遗忘之前,后续节点被被保留在队列的外链中。h.forgetNext();break;}// advance and retry// 前进及重试// 获取最新的头节点(快照)及其后继节点(快照),并进行判断:// (h = head) == null:判断队列中是否已经没有元素;// (q = h.next) == null:判断队列中是否只有一个元素;// !q.isMatched():判断后继节点(快照)是否已被匹配。// 满足上述条件说明由于不断的前进,当前懈怠值已小于2,不满足移除/拿取的前提条件,直接结束该操作;否则就要继续前进直// 至成功或懈怠值不满足条件。if ((h = head) == null || (q = h.next) == null || !q.isMatched())// unless slack < 2// 除了slack < 2break;}// 唤醒匹配的队列节点所在的线程,并返回其项/元素。LockSupport.unpark(p.waiter);return LinkedTransferQueue.<E>cast(item);}}// 获取当前节点(快照)的后继节点(快照),以此向后进行遍历并匹配。如果当前节点(快照)已从队列中移除/拿取,则重新从头// 开始匹配。Node n = p.next;// Use head if p offlist// 如果p已脱离则使用头节点。p = (p != n) ? n : (h = head);}// No matches available// 无匹配无用(即队列中没有匹配的元素,可能是一开始就没有,也可能是CAS失败后没有)if (how != NOW) {// 将操作封装为一个节点,用于加入队列。if (s == null)s = new Node(e, haveData);// 尝试追加,即将节点加入队列中。Node pred = tryAppend(s, haveData);// 如果返回null,说明节点因为模式的原因没有追加成功,需要重新开始整个流程进行重试。if (pred == null)// lost race vs opposite mode// 迷失竞争与相反的模式continue retry;// 节点入队成功后,如果不为异步,则需等待,直至有节点与之当前线程的节点匹配为止。if (how != ASYNC)return awaitMatch(s, pred, e, (how == TIMED), nanos);}// not waiting// 不等待return e;}}/*** Tries to append node s as tail.* 尝试追加节点s作为尾节点** @param s        the node to append 用于追加的节点* @param haveData true if appending in data mode 如果在数据模式中添加则为true(即添加的节点是数据(插入/放置)节点)* @return null on failure due to losing race with append in different mode, else s's predecessor, or s itself if no predecessor* 在不同的模式中由于迷失竞争而失败则返回null,否则(即成功)返回s的前驱节点,或者如果没有前驱节点则为s自己* @Description: 名称:尝试追加* @Description: 作用:执行追加节点步骤,将指定节点加入链接迁移队列的尾部,并返回其前驱节点。如果返回为null,说明有其它线程并* @Description: 发追加了相反模式的节点导致当前线程未追加成功,重新执行互补/匹配步骤。* @Description: 逻辑:从链接迁移队列的当前尾节点向后遍历,找到真正的最后节点。判断最后节点是否支持添加新节点(即是否是相反* @Description: 模式的未匹配/取消节点),如果支持尝试追加。由于并发的原因该操作可能失败,因此需要不断尝试,直至追加成功或最* @Description: 后节点不支持追加新节点为止。追加成功后如果满足至少2个懈怠节点则更新尾节点,该操作同样会因为并发而失效,因此* @Description: 需要不断尝试,直至更新成功或懈怠节点的数量小于2为止,最后返回新追加节点的前驱节点,也就是最后节点;如果最后* @Description: 节点不支持追加新节点,则直接返null,表示追加失败,需要重新执行互补/匹配步骤。*/private Node tryAppend(Node s, boolean haveData) {// move p to last node and append// 移动p至最后节点并追加(即将p指向最后节点,并在此基础上添加节点)。// 变量t表示尾节点(快照),变量p表示最后节点(快照)。在初始状态下,两者是相等的。for (Node t = tail, p = t; ; ) {// temps for reads of next & tail// 读取下个尾节点的临时(快照)Node n, u;if (p == null && (p = head) == null) {// p == null && (p = head) == null表示当前队列中不存在节点,因此需要添加首个节点。其中(p = head) == null属于二次判断,// 因为队列在入队首个节点成功,并不会将该节点设置为尾节点,所以需要通过p = head来获取最后节点(快照)的值,确// 保队列中真正不存在任何节点。// 入队头节点,但不会将之设置为尾节点,此时tail(尾)字段值为null。if (casHead(null, s))// initialize// 初始化return s;} else if (p.cannotPrecede(haveData))// lost race vs opposite mode// 迷失竞争与相反模式// 在确定队列存在节点的情况下,判断最后节点(快照)能否成为新节点的前驱节点。如果不可以则直接返回null,意味着在// 此期间有与新节点相反模式的节点加入队列。由于队列中可能存在着可匹配的节点,因此新节点不可以加入队列中。return null;else if ((n = p.next) != null)// not last; keep traversing// 非最后;保持遍历// stale tail// 过期尾节点// restart if off list// 如果已脱离队列则重试// 如果最后节点(快照)存在后继节点,意味着由于并发的原因,已经有节点被追加入队列,最后节点(快照)已不是真正// 的最后节点,因此需要重新定位。// p != t :最后节点(快照)与尾节点(快照)不相等,说明最后节点(快照)已经经历过遍历式的重定位;// t != (u = tail):尾节点(快照)与尾节点不相等,说明由于并发,已经有新加入的节点被设置为尾节点。// 如果满足上述两个条件,则将最后节点(快照)赋值为新的尾节点(快照)。这是因为一般来说尾节点后面只会存在最多// 一个节点(如果大于就会触发尾节点的更新,因此很快就会再次回到最多只有一个节点的情况)。因此如果发现最后节点// (快照)存在后继节点,并且最后节点(快照)等于尾节点(快照)(意味着最后节点(快照)没有经历过遍历式的重定// 位),通常只需将最后节点(快照)向后遍历定位一次即可,这种情况是最普遍的,有助于减少 t != (u = tail)条件的判断,// 有助于在循环中小幅度的提高性能。但如果在此期间尾节点也发生了更新, 尾节点(快照)与尾节点之间就至少有两个节// 点的差距,那最后节点(快照)与尾节点之间就至少有一个节点的差距(现 实情况可能更多) ,为了快速定位最后节点,// 遍历一次次的遍历,故而直接最后节点(快照)赋值为尾节点。p = p != t && t != (u = tail) ? (t = u) : (p != n) ? n : null;else if (!p.casNext(null, s))// re-read on CAS failure// 当CAS失败时重读取(新的最后节点)(即定位定位新的最后节点(快照))// 如果最后节点(快照)没有后继节点,尝试执行CAS操作将新节点设置为其后继节点使其加入队列。如果 CAS操作失败,// 则说明存在竞争,将最后节点(快照)赋值为其后继节点。新的最后节点(快照)未必是真的最后节点,因为可能有多个线// 程都添加节点。但是通过这种方式再配合循环的其它逻辑便可以不断的向后遍历,直至使最后节点(快照)定位到真正的最// 后节点为止。p = p.next;else {// update if slack now >= 2// 如果当前懈怠 >= 2则更新(尾节点)// 如果新节点已成功加入队列中,则需要设置新的尾节点。该操作并非每次追加后都要执行,基于懈怠理念,只有当懈怠 >= 2// 时才会更新尾节点,即尾节点(快照)后还存在至少两个节点。在源码中,符合该要求的判断条件为p != t,其意味着最后节// 点(快照)不等于尾节点(快照),即两者之间至少存在着一个节点的差距(包括尾节点(快照)为null的情况)。并且由于// 刚成功添加了新节点,意味着最后节点与尾节点(快照)之间至少有两个节点的差距,从而满足了懈怠的条件。if (p != t) {// (tail != t || !casTail(t, s)):这两种情况都表示有其它线程并发更新了尾节点,当前更新失败,需要重试;//     tail != t :表示尾节点已被并发更新,不满足更新条件,因为我们无法在新尾节点的基础上将新节点设置为尾节点。事实//     上,在队列中,新尾节点很可能位于新节点的后方;//     !casTail(t, s):表示将新节点设置为尾节点失败;// (t = tail) != null:表示尾节点不为null,这是向尾节点后方遍历的基础条件。但事实上,只要尾节点经历过一次更新,就不// 可能为null,因此该条件似乎有所多余;// (s = t.next) != null:表示尾节点(快照)存在后继节点,可以继续更新尾节点;// (s = s.next) != null:表示尾节点(快照)的后继节点还存在后继节点,可以继续更新尾节点;// s != t:表示尾节点(快照)并没有脱离队列,可以用于更新尾节点。// 如果上述情况全部满足,则说明更新头节点没有成功(或因为不满足条件而没有执行),并且可通过操作满足继续更新头// 节点的条件,因此需要不断的更新下去,直至更新成功或无法满足更新条件为止。//// 操作的开始会尝试将新节点设置为尾节点,但如果尾节点已被并发更新,则尝试将尾节点的后后继节点设置为尾节点,即// 在当前的基础上向后移动两位(前提是尾节点(快照)没有脱离队列)。因此,可知尾节点的更新至少会在当前尾节点(// 快照)的基础上跳跃两位(如果成功更新的话)。while ((tail != t || !casTail(t, s)) && (t = tail) != null &&// advance and retry// 前进并重试(s = t.next) != null && (s = s.next) != null && s != t) ;}// 返回新节点的前继节点p。return p;}}}/*** Spins/yields/blocks until node s is matched or caller gives up.* 自旋/让步/阻塞直至节点s匹配或调用者放弃** @param s     the waiting node 等待中的节点* @param pred  the predecessor of s, or s itself if it has no predecessor, or null if unknown (the null case does not occur in any*              current calls but may in possible future extensions)*              s的前驱节点,或者如果其没有前驱节点则为s本身,或者如果未知则放回null(null的情况不会发生于任意当前调用中但*              可能发生在未来的拓展中)* @param e     the comparison value for checking match 检查匹配的比较值* @param timed if true, wait only until timeout elapses 如果true,只等待到超时消逝* @param nanos timeout in nanosecs, used only if timed is true 纳秒超时,是当timed为true时使用* @return matched item, or e if unmatched on interrupt or timeout 匹配项,如果因为中断或超时而未匹配则返回e* @Description: 名称:等待匹配* @Description: 作用:执行线程自旋/等待步骤,对新追加匹配节点执行自旋后挂起,等待匹配节点被互补操作所互补/匹配。如果互补/匹* @Description: 配匹配成功则返回互补节点的项;如果因为中断或超时等原因未匹配成功则返回匹配节点的项。* @Description: 逻辑:根据处理器核数及匹配节点的位置获取自旋数量并执行自旋,在自旋结束后执行等待。在自旋及等待期间,如果有互* @Description: 补操作与之互补/匹配,则在互补/匹配完成返回互补节点的项;如果因为线程中断或等待超时等原因未互补/匹配成功则返* @Description: 回匹配节点的项。*/private E awaitMatch(Node s, Node pred, E e, boolean timed, long nanos) {// 确定超时时间。final long deadline = timed ? System.nanoTime() + nanos : 0L;Thread w = Thread.currentThread();// initialized after first item and cancel checks// 在首次项和取消检查之后初始化int spins = -1;// bound if needed// 如果需要则有界ThreadLocalRandom randomYields = null;for (; ; ) {// 获取新节点的项Object item = s.item;// matched// 匹配// 如果新节点的项与用于检查的项不相等,则说明匹配结束。if (item != e) {// assert item != s;// 断言 项不等于s// avoid garbage// 避免垃圾s.forgetContents();return LinkedTransferQueue.<E>cast(item);}// cancel// 取消// 如果线程中断或执行超时,则节点需要被取消。如果取消成功(即通过CAS操作将新节点的项设置为自身),则需要将新节点// 的原项返回表示互补/匹配操作失败。但在返回之前,需要先断开新节点对各项资源(后继引用/线程对象)的引用,以帮助GC// 对新节点本身及资源的回收。if ((w.isInterrupted() || (timed && nanos <= 0)) && s.casItem(e, s)) {// 断开拼接。unsplice(pred, s);return e;}if (spins < 0) {// establish spins at/near front// 在/接近前部则建立旋转// 获取自旋值,并且如果需要自旋,实例化一个ThreadLocalRandom实例。if ((spins = spinsFor(pred, s.isData)) > 0)randomYields = ThreadLocalRandom.current();} else if (spins > 0) {// spin// 自旋--spins;// 在自旋的过程中,偶尔会让出CPU资源让其它任务得以执行,应该出于避免线程饥饿的考虑。if (randomYields.nextInt(CHAINED_SPINS) == 0)// occasionally yield// 偶尔让步Thread.yield();} else if (s.waiter == null) {// request unpark then recheck// 请求唤醒则重检查// 当所有自旋次数耗尽后,在等待之前保存自身线程,用于互补节点将自身唤醒使用。s.waiter = w;} else if (timed) {// 进行定时等待。nanos = deadline - System.nanoTime();if (nanos > 0L)LockSupport.parkNanos(this, nanos);} else {// 进行永久等待。LockSupport.park(this);}}}/*** Returns spin/yield value for a node with given predecessor and data mode. See above for explanation.* 返回指定前驱节点及数据模式的节点的自旋/让步值。解释看上文。** @Description: 名称:自旋关于* @Description: 作用:针对前驱节点的状态设置当前节点的自旋值。* @Description: 逻辑:判断计算机是否是多核处理器,如果不是则直接返回false,因为自旋只在多核处理器下生效。在多核处理器的前提下,* @Description: 如果前驱节点与指定节点属于阶段性改变,则说明指定节点是首个可匹配节点,被互补/匹配的可能性极大,并且相比链接迁* @Description: 移队列前部的情况其需要更多的自旋数来等待互补/匹配(这是经验性的结论),因此其自旋数也是最大的,为128 + 64 = 192;* @Description: 如果前驱节点已被匹配/取消,则指定节点大概率是首个可匹配节点,对于这种情况的自旋数是128;如果前驱节点依然在自旋* @Description: 中,则指定节点大概率位于链接迁移队列前部,这种情况下的自旋数量为64。*/private static int spinsFor(Node pred, boolean haveData) {// 如果当前机器是多个服务器并且存在前驱节点。if (MP && pred != null) {// 如果前驱节点的模式与当前节点的模式不相同,这说明前驱节点必然已被匹配/取消,当前节点是自身所属模式在队列中的首个节// 点。对于队列中的首个可匹配节点,其自旋数为128 + 64 = 192if (pred.isData != haveData)// phase change// 阶段性改变return FRONT_SPINS + CHAINED_SPINS;// 如果前驱节点与当前节点的模式相同,并且前驱节点已被匹配/取消,则当前节点大概率在头部,因此设置其自旋值为128。if (pred.isMatched())// probably at front// 可能在前部return FRONT_SPINS;// 如果前驱节点的waiter(等待者)字段为null,这前驱节点还未等待,说明可能在自旋中,说明其大概率比较靠前,因此设置当前节// 点的自旋值为64。if (pred.waiter == null)// pred apparently spinning// 前驱节点表面上自旋return CHAINED_SPINS;}return 0;}/* -------------- Traversal methods -------------- *//* -------------- 遍历方法 -------------- *//*** Returns the successor of p, or the head node if p.next has been linked to self, which will only be true if traversing with a stale pointer* that is now off the list.* 返回p的后继节点,或如果p.next已经自引用(只有在遍历一个已经不在列表中的旧指针时才会成立。)则返回头节点。** @Description: 后继:获取指定节点的后继节点。如果指定节点已自引用,即其已从队列中脱离则返回其本身。*/final Node succ(Node p) {Node next = p.next;return (p == next) ? head : next;}/*** Returns the first unmatched node of the given mode, or null if none.  Used by methods isEmpty, hasWaitingConsumer.* 返回指定模式的首个未匹配节点,或如果不存在就返回null。通过isEmpty(),hasWaitingConsumer()方法使用。** @Description: 模式的首个:获取指定模式的首个未匹配节点,如果不存在则返回null。*/private Node firstOfMode(boolean isData) {// 从头节点开始向后遍历,当查找到首个未匹配节点时判断模式是否相同,相同则返回,否则返回null。由于队列中可允许存在一种模// 式的未匹配节点,因此如果首个未匹配节点的模式不同,则就没有符合条件的节点。for (Node p = head; p != null; p = succ(p)) {if (!p.isMatched())return (p.isData == isData) ? p : null;}return null;}/*** Version of firstOfMode used by Spliterator. Callers must recheck if the returned node's item field is null or self-linked before using.* 由分割迭代器使用的firstOfMode()方法的版本。如果返回的节点的项字段是null或者在使用之前自引用,调用者必须重新检查。** @Description: 首个数据节点:获取首个未匹配的数据节点,如果不存在则返回null。*/final Node firstDataNode() {// 从头节点开始遍历。for (Node p = head; p != null; ) {Object item = p.item;if (p.isData) {// 如果找到了未匹配的数据节点则返回。if (item != null && item != p)return p;} else if (item == null)// 如果当前节点是请求节点,并且未被匹配,说明队列中不存在未匹配的数据节点,直接结束遍历。break;// 向后遍历。if (p == (p = p.next))p = head;}return null;}/*** Returns the item in the first unmatched node with isData; or null if none.  Used by peek.* 返回在首个未匹配数据节点中的项;如果不存在则返回null。通过peek()方法调用。** @Description: 首个数据元素:获取首个未匹配的数据节点的元素,如果不存在则返回null。*/private E firstDataItem() {// 从头节点开始向后遍历。for (Node p = head; p != null; p = succ(p)) {Object item = p.item;if (p.isData) {// 如果当前节点是未匹配的数据节点,则返回项。if (item != null && item != p)return LinkedTransferQueue.<E>cast(item);} else if (item == null)// 如果当前节点是请求节点,并且未匹配,说明队列中不存在未匹配的数据节点,直接返回null。return null;}return null;}/*** Traverses and counts unmatched nodes of the given mode. Used by methods size and getWaitingConsumerCount.* 遍历并计数未匹配的指定模式节点。通过size()及getWaitingConsumerCount()方法调用。** @Description: 模式的总数:获取未匹配的指定模式节点的数量。*/private int countOfMode(boolean data) {// 从头节点开始遍历。int count = 0;for (Node p = head; p != null; ) {if (!p.isMatched()) {// 如果发现了未匹配的并且是非指定模式的节点,说明当前队列中不存在未匹配的指定模式的节点,因此直接返回0。if (p.isData != data)return 0;// 如果当前节点是未匹配的指定模式的节点,进行计数。如果计数的数量达到了int类型的最大值,则直接返回。事实上,链表迁移// 队列是由于没有size(大小)字段,因此可以称之为真正的无界队列,即元素的数量只与堆内存有关,因此可能保存大于int类型// 最大值数量的元素,但是由于size()方法返回值的限制,其最多也只能返回int类型的最大值。if (++count == Integer.MAX_VALUE)// saturated// 饱和的break;}// 向后遍历。Node n = p.next;if (n != p)// 如果当前节点没有出队,则直接向后遍历。p = n;else {// 如果当前节点已经出队,则重置计数,重新从头节点开始遍历及计数。count = 0;p = head;}}return count;}/* -------------- Removal methods -------------- *//*** Unsplices (now or later) the given deleted/cancelled node with the given predecessor.* 断开(现在或以后)指定删除/取消节点与指定前前驱节点的拼接。** @param pred a node that was at one time known to be the predecessor of s, or null or s itself if s is/was at head*             曾经已知的s的前驱节点,或如果s现在/过去是头节点则为null或s本身* @param s    the node to be unspliced 断开拼接的节点* @Description: 名称:断开拼接* @Description: 作用:断开已取消节点的拼接,将之从队列中内部移除,并视情况累积清理选票和触发完全清理。* @Description: 逻辑:通过将前驱节点的后继节点通过CAS操作设置为已取消节点的后继节点来将已取消节点内部出队。在这个过程中,有* @Description: 两种情况可能会使已取消节点发生遗留:一是已取消节点是最后节点。最后节点在内部移除过程中可能会受到追加操作的并* @Description: 发影响导致新节点可能追加到内部移除的垃圾节点上,因此人为强制的保留;二是前驱节点已在进行内部移除。如果前驱节* @Description: 点本身也在执行内部移除,这种情况下前前驱节点就会持有已取消节点的引用。如此就可能存在前驱节点和前前驱节点两个* @Description: 节点持有了已取消节点的后继引用,虽然后续会将前驱节点指向已取消节点的后继节点,但由于前前驱节点依然指向已取消* @Description: 节点,因此已取消节点依然存在于队列中,这就是所谓的遗留。由于遗留的存在,那么就需要对之特殊的处理,但是有两种* @Description: 情况可以避免:一是前驱节点已实际移除/拿取;二是已取消节点已实际移除/拿取。这两种情况一个表示已取消节点未被遗* @Description: 留(已取消节点可以被实际移除/拿取说明其还没有被内部移除,既然没有内部移除,则已取消节点遗留就必然不会发生);* @Description: 一个表示已取消节点(如果真的遗留的话)已被实际移除/拿取。如果这两个情况都不满足,则说明已取消节点可能遗留,* @Description: 因此需要特殊处理。所谓的特殊处理是一种投票机制,当判断可能发生遗留时进行一次选票累积,当选票累积的数量触达上* @Description: 限(32)时触发完全清理,所谓的完全清理就是从头到尾的进行一次清理。*/final void unsplice(Node pred, Node s) {// forget unneeded fields// 遗忘不必要的字段(后继引用/线程对象)s.forgetContents();/** See above for rationale. Briefly: if pred still points to s, try to unlink s.  If s cannot be unlinked, because it is trailing node or pred* might be unlinked, and neither pred nor s are head or offlist, add to sweepVotes, and if enough votes have accumulated, sweep.* 看上文的逻辑依据。简短的:如果pred依然指向s,尝试断开s的链接。如果s无法断开连接,因为它是一个最后节点或pred可能已断* 开连接,以及如果pred和s都既不是头,也没有脱离列表,则新增至清理选举,并且如果选票积累足够,则进行清理。*/// pred != null:前驱节点pred不为null,表示节点s存在前驱节点;// pred != s:前驱节点pred不等于s, 表示节点s不是头节点;// pred.next == s:前驱节点pred的后继节点为节点s,表示前驱节点pred没有从队列中被移除/拿取;// 只有满足上述三个条件的情况下才需要去断开节点s与其前驱节点pred的引用。if (pred != null && pred != s && pred.next == s) {// 获取节点s的后继节点n。Node n = s.next;// n == null:后继节点n为null,表示节点s没有后继节点,是最后节点;节点s是最后节点的情况下不可以被移除,因为被用以作为追// 加节点的基础,因此只能在后续中移除。// n != s && pred.casNext(s, n) && pred.isMatched():满足下述三个条件的情况下,即使已经断开了前驱节点与s的链接,其也依// 然可能保留在队列中。具体的情况是前驱节点已从队列中脱离,但还没有通过自引用断开与s的链接。如此一来就存在两个节点持// 有s的后继引用,一个是前驱节点(如今的前驱节点(快照)),一个是前前驱节点(如今的前驱节点)。这样,即使后续断开了// 前驱节点与s的链接,但依然保留着前前驱节点与s的链接,即s依然保留在队列中。因此需要正对这种情况进行处理,在后继中进// 行移除。//     n != s:后继节点n不等于节点s,表示节点s没有移除/拿取出队列;//     pred.casNext(s, n):后继节点n通过CAS操作设置为前驱节点s的后继节点成功,意味着已经成功从队列中移除了节点s;//     pred.isMatched():前驱节点pred已经匹配/取消,意味着前驱节点可能从队列中移除。if (n == null || (n != s && pred.casNext(s, n) && pred.isMatched())) {// check if at, or could be, head// 如果在或者可以在头部则检查// 针对可能造成节点s保留的两种情况,需要进行特殊的处理。该操作的难点在于我们无法确定节点s是否真的被遗留在队列中,其// 只是可能被遗留,而非一定被遗留。因此在下述for循环中会通过头部出队已匹配/取消节点的方式尽可能的将节点移除,一方面// 可以辅助互补/匹配操作的头部出队,另一方面也可以尝试在此过程中奖可能遗留的节点s从队列中移除。如果可能通过这种方式// 将整个队列情况,则说明节点s一定不曾遗留在队列中,从而避免使用清理的特殊逻辑。for (; ; ) {// 获取头节点(快照)Node h = head;// h == pred:头节点(快照)等于前驱节点pred,意味着不会有前前驱节点持有节点s引用的情况,无需特殊处理;// h == s:头节点(快照)等于节点s,意味着没有队列内的节点再持有节点s的后继引用(队列外可能有,但也会很快消失),无// 需特殊处理。// h == null:头节点(快照)为null,意味着当前队列为空...会有这种情况?// 上述三种情况说明即使真的发生了节点保留,也无需进行特殊处理,可以直接宣告断开拼接操作的结束。if (h == pred || h == s || h == null)// at head or list empty// 在头部或列表为空return;// 如果不满足上述任意一个条件,说明节点s很可能会保留在队列中,并且因为经历了可能失败的断开拼接方式的出队,因此无法// 再次通过该方式出队,只能依赖其它方式。此时判断头节点(快照)是否已被匹配/取消,如果没有匹配/取消,说明队列不是一// 个空队列,则节点s就很有可能遗留在队列中。此时需要结束循环,采用特殊逻辑来进行清理。if (!h.isMatched())break;// 如果头节点已被匹配/取消,获取头节点(快照)的后继节点(快照),判断其是否为null,如果为null,说明队列基本已经是空// 队列(因为只剩下一个头节点了,而由于懈怠与保留头节点用于追加的缘故,其不会被移除),因此可知并没有发生节点s被遗// 留在队列中的情况,可以直接宣告断开拼接操作的结束。Node hn = h.next;if (hn == null)// now empty// 空队列return;// hn != h:头节点(快照)不等于头节点(快照)的后继节点(快照),意味着头节点(快照)没有出队;// casHead(h, hn):将头节点(快照)的后继节点(快照)通过CAS操作替换为头节点,意味着我们帮助移除了一个已匹配/取消// 的头节点。将已匹配/取消的头节点出队,该操作一方面可以帮助辅助头部出队;另一方面在此操作中可能会将遗留的节点s从队// 列中移除,从而避免使用特殊的处理机制。if (hn != h && casHead(h, hn))// advance head// 前进头节点// 将已出队的头节点设置为自引用,帮助GC。h.forgetNext();}// recheck if offlist// 检查是否脱离队列// pred.next != pred:前驱节点pred没有(从头部)脱离队列,意味着前前驱节点也可能没有从队列中头部移除;// s.next != s:节点s没有(从头部)脱离队列,意味着其可能遗留在队列中;// 只有在满足上述两个条件的情况下才会执行清理的特殊逻辑,不然只会大概率白白的执行无意义的操作。if (pred.next != pred && s.next != s) {// sweep now if enough votes// 如果当前票数足够则清理for (; ; ) {// 获取清理票数(快照)。int v = sweepVotes;if (v < SWEEP_THRESHOLD) {// 如果清理票数(快照)小于阈值,只是通过CAS操作增加票数,而不是执行清理。这说明就算是遗留,数量也不会太多,可以// 暂时的接受,等到数量积攒到一定的程度,准确的说是票数积攒到阈值后再进行清理。事实上,由于队列在不断的运行,先前// 遗留的节点很有可能已经被正常的移除了。if (casSweepVotes(v, v + 1))break;} else if (casSweepVotes(v, 0)) {// 如果清理票数(快照)已经触达了阈值,则将清理选票清零,并随后执行清理。sweep();break;}}}}}}/*** Unlinks matched (typically cancelled) nodes encountered in a traversal from head.* 断开在一次从头开始的遍历中遭遇的已匹配的(通常是已取消的)节点的链接。** @Description: 名称:完全清理* @Description: 作用:完整遍历整个链接迁移队列(不含头节点和最后节点),清理其中已匹配/取消节点。* @Description: 逻辑:遍历整个链接迁移队列,当发现已匹配/取消节点通过断开链接的方式将之内部移除。但遍历时不会遍历头节点和最后* @Description: 节点,因为头节点没有前驱节点不能内部移除;而最后节点要保持追加操作的正常执行不允许被移除。*/private void sweep() {// 执行一次从头到尾的遍历,由于懈怠、后继追加等综合原因,我们不会遍历队列第一及最后一个节点。for (Node p = head, s, n; p != null && (s = p.next) != null; ) {if (!s.isMatched())// Unmatched nodes are never self-linked// 为匹配(取消)节点永不链接// 如果当前节点没有被匹配/取消,则继续向后遍历。p = s;else if ((n = s.next) == null)// trailing node is pinned// 最后节点被牵制// 如果发现下个节点后是最后节点,则直接结束整个遍历。因为最后一个节点因为懈怠及后续追加等原因是不允许被移除的,因此无需检// 查是否匹配/取消。break;else if (s == n)// stale// 过期// No need to also check for p == s, since that implies s == n// 不需要检查p == s,因为这意味着s == n(如果一个节点从头部出队了,则意味着其前驱节点也一定与队列完全脱离)// 如果下个节点已从队列中移除,则从头开始重新遍历。p = head;else// 如果节点已被匹配/取消,则进行清理。p.casNext(s, n);}}/*** Main implementation of remove(Object)* remove(Object)的主要实现** @Description: 名称:找到并移除* @Description: 作用:从链接迁移队列中找到首个指定元素并移除。成功移除则返回true;否则返回false。* @Description: 逻辑:遍历链接迁移队列,如果没有找到指定元素则返回false;如果发现了指定元素将通过内部移除的方式将之移除。在内* @Description: 部移除之前,节点会先被强制互补/匹配,作用是避免节点在移除的过程中被互补/匹配。*/private boolean findAndRemove(Object e) {if (e != null) {// 从头节点开始向后遍历。for (Node pred = null, p = head; p != null; ) {Object item = p.item;if (p.isData) {// 如果当前节点是数据节点,判断是否匹配/取消,并判断元素是否相等,以及令其强制匹配。强制匹配的目的是避免在断开拼接过程// 中的并发干扰。防止有其它线程在断开拼接过程中将之匹配。if (item != null && item != p && e.equals(item) && p.tryMatchData()) {unsplice(pred, p);return true;}} else if (item == null)// 如果是为未匹配的请求节点,说明队列中不存在未匹配的数据节点,直接结束循环。break;// 如果在遍历过程中当前节点脱离队列,则从头开始重新遍历。pred = p;if ((p = p.next) == pred) {// stale// 过期的pred = null;p = head;}}}return false;}/*** Creates an initially empty {@code LinkedTransferQueue}.* 创建一个初始化空链接迁移队列。** @Description: 名称:~* @Description: 作用:创建一个链接迁移队列。* @Description: 逻辑:~*/public LinkedTransferQueue() {}/*** Creates a {@code LinkedTransferQueue} initially containing the elements of the given collection, added in traversal order of the collection's* iterator.* 创建一个初始包含指定集元素的链接迁移队列,新增按指定集的迭代器遍历顺序。** @param c the collection of elements to initially contain 初始化包含元素的集* @throws NullPointerException if the specified collection or any of its elements are null*                              空指针异常:如果指定集或其中任意元素为null* @Description: 名称:~* @Description: 作用:创建一个包含指定集中所有元素的链接迁移队列。* @Description: 逻辑:实例化将指定集中的元素添加至该链接迁移队列。*/public LinkedTransferQueue(Collection<? extends E> c) {this();addAll(c);}/*** Inserts the specified element at the tail of this queue. As the queue is unbounded, this method will never throw {@link IllegalStateException} or* return {@code false}.* 在队列的尾部插入指定元素。由于队列是无界的,该方法永远不会抛出非法状态异常或返回false。** @return {@code true} (as specified by {@link Collection#add}) 如果* @throws NullPointerException if the specified element is null*                              空指针异常:如果指定元素为null* @Description: 名称:新增* @Description: 作用:向链接迁移队列的队尾添加指定元素。该方法是插入/放置操作"异常"形式的实现,当链接迁移队列存在空余容量时插入/* @Description: 放置成功并返回true;否则抛出非法状态异常。虽说定义如此,但实际由于链接迁移队列是真正的无界队列,最大容量理只受限* @Description: 于堆内存的大小,故而永远不会抛出非法状态异常,而只会在堆内存不足时抛出内存溢出异常。* @Description: 逻辑:~*/public boolean add(E e) {// 由于是插入/放置操作,对应的是数据节点,而数据节点虽然可以加入队列。但是是不会阻塞的。xfer(e, true, ASYNC, 0);return true;}/*** Inserts the specified element at the tail of this queue. As the queue is unbounded, this method will never return {@code false}.* 在队列的尾部插入指定元素。该队列是无界的,该方法永远不会返回false。** @return {@code true} (as specified by {@link Queue#offer}) true(由Queue#offer()指定)true* @throws NullPointerException if the specified element is null*                              空指针异常:如果指定元素为空* @Description: 名称:提供* @Description: 作用:向链接迁移队列的队尾添加指定元素。该方法是插入/放置操作"特殊值"形式的实现,当链接迁移队列存在空余容量时插入/* @Description: 放置成功并返回true;否则抛出非法状态异常。虽说定义如此,但实际由于链接迁移队列是真正的无界队列,最大容量只受限于* @Description: 堆内存的大小,故而永远不会抛出非法状态异常,而只会在堆内存不足时抛出内存溢出异常。* @Description: 逻辑:~*/public boolean offer(E e) {// 由于是插入/放置操作,对应的是数据节点,而数据节点虽然可以加入队列。但是是不会阻塞的。xfer(e, true, ASYNC, 0);return true;}/*** Inserts the specified element at the tail of this queue. As the queue is unbounded, this method will never block.* 在队列的尾部插入指定元素。由于队列是无界的,该方法永远不会阻塞。** @throws NullPointerException if the specified element is null 如果指定节点为null* @Description: 名称:放置* @Description: 作用:向链接迁移队列的队尾添加指定元素。该方法是插入/放置操作"阻塞"形式的实现,当链接迁移队列存在空余容量时插入/* @Description: 放置成功并返回true;否则阻塞至有空余容量。虽说定义如此,但实际由于链接迁移队列是真正的无界队列,最大容量只受限于* @Description: 堆内存的大小,故而永远不会阻塞,而只会在堆内存不足时抛出内存溢出异常。* @Description: 逻辑:~*/public void put(E e) {// 由于是插入/放置操作,对应的是数据节点,而数据节点虽然可以加入队列。但是是不会阻塞的。xfer(e, true, ASYNC, 0);}/*** Inserts the specified element at the tail of this queue. As the queue is unbounded, this method will never block or return {@code false}.* 在队列的尾部插入指定元素。由于队列是无界的,该方法永远不会阻塞或返回false。** @return {@code true} (as specified by {@link BlockingQueue#offer(Object, long, TimeUnit) BlockingQueue.offer}) true* @throws NullPointerException if the specified element is null 如果指定节点为null* @Description: 名称:放置* @Description: 作用:向链接迁移队列的队尾添加指定元素。该方法是插入/放置操作"超时"形式的实现,当链接迁移队列存在空余容量时插入/* @Description: 放置成功并返回true;否则在指定的等待时间内阻塞至有空余容量,超出等待时间则返回false。虽说定义如此,但实际由于链接* @Description: 迁移队列是真正的无界队列,最大容量只受限于堆内存的大小,故而永远不会阻塞或返回false,而只会在堆内存不足时抛出内存* @Description: 溢出异常。* @Description: 逻辑:~*/public boolean offer(E e, long timeout, TimeUnit unit) {xfer(e, true, ASYNC, 0);return true;}/*** Transfers the element to a waiting consumer immediately, if possible.* 如果可能,立即迁移元素至一个等待中的消费者。* <p>* More precisely, transfers the specified element immediately if there exists a consumer already waiting to receive it (in {@link #take} or timed* {@link #poll(long, TimeUnit) poll}), otherwise returning {@code false} without enqueuing the element.* 更准确的,如果已经存在一个消费者等待(即存在执行中的take()方法)接收或定时(即存在执行中的poll(long, TimeUnit)/poll()方法)则立* 即迁移指定元素,否则不入队元素返回false。** @throws NullPointerException if the specified element is null 如果指定节点为null* @Description: 名称:尝试迁移* @Description: 作用:将元素迁移至拿取者。该方法是迁移操作"特殊值"形式的实现,如果链接迁移队列中已存在等待中的拿取者则将元素迁移* @Description: 并返回true;否则直接舍弃元素并返回false。* @Description: 逻辑:~*/public boolean tryTransfer(E e) {// NOW的意思就是只执行互补/匹配阶段,而不执行追加节点与线程自旋/等待阶段。return xfer(e, true, NOW, 0) == null;}/*** Transfers the element to a consumer, waiting if necessary to do so.* 迁移指定元素至一个消费者,如果必要则等待直至执行。* <p>* More precisely, transfers the specified element immediately if there exists a consumer already waiting to receive it (in {@link #take} or timed* {@link #poll(long, TimeUnit) poll}), else inserts the specified element at the tail of this queue and waits until the element is received by a consumer.* 更准确的,如果已经存在一个消费者等待(即存在执行中的take()方法)接收或定时(即存在执行中的poll(long, TimeUnit)/poll()方法)则立即迁* 移指定元素,否则在队列的尾部插入元素并等待直至元素被一个消费者接收。** @throws NullPointerException if the specified element is null 如果指定节点为null* @Description: 名称:迁移* @Description: 作用:将元素迁移至拿取者。该方法是迁移操作"阻塞"形式的实现,如果链接迁移队列中已存在等待中的拿取者则将元素迁移并返回* @Description: true;否则将元素插入链接迁移队列尾部并阻塞,直至有拿取者带来接收元素。* @Description: 逻辑:~*/public void transfer(E e) throws InterruptedException {if (xfer(e, true, SYNC, 0) != null) {Thread.interrupted(); // failure possible only due to interruptthrow new InterruptedException();}}/*** Transfers the element to a consumer if it is possible to do so before the timeout elapses.* 如果可能在超时(时间)消逝之前执行则迁移元素至一个消费者。* <p>* More precisely, transfers the specified element immediately if there exists a consumer already waiting to receive it (in {@link #take} or timed {@link* #poll(long, TimeUnit) poll}), else inserts the specified element at the tail of this queue and waits until the element is received by a consumer,* returning {@code false} if the specified wait time elapses before the element can be transferred.* 更准确的,如果已经存在一个消费者等待(即存在执行中的take()方法)接收或定时(即存在执行中的poll(long, TimeUnit)/poll()方法)则立即迁* 移指定元素,否则在队列的尾部插入元素并等待直至元素被一个消费者接收,如果在元素可以被迁移前指定等待时间消逝则返回false。** @throws NullPointerException if the specified element is null 如果指定节点为null* @Description: 名称:尝试迁移* @Description: 作用:将元素迁移至拿取者。该方法是迁移操作"超时"形式的实现,如果链接迁移队列中已存在等待中的拿取者则将元素迁移并返回* @Description: true;否则将元素插入链接迁移队列尾部并阻塞指定时间,如果在指定时间超时前有拿取者到来并接收元素则返回false。否则舍弃元* @Description: 素并返回false。* @Description: 逻辑:~*/public boolean tryTransfer(E e, long timeout, TimeUnit unit) throws InterruptedException {if (xfer(e, true, TIMED, unit.toNanos(timeout)) == null)return true;if (!Thread.interrupted())return false;throw new InterruptedException();}/*** @Description: 名称:有等待中的消费者* @Description: 作用:判断链接迁移队列中是否存在等待中的移除/拿取操作,有则返回true;否则返回false。* @Description: 逻辑:遍历整个链接迁移队列,查找首个未匹配的请求节点,如果未查找到或者查找到了未匹配的数据节点,则说明不存在。*/public boolean hasWaitingConsumer() {return firstOfMode(false) != null;}/*** @Description: 名称:获取等待中的消费者总数* @Description: 作用:获取链接迁移队列中等待中的移除/拿取操作的总数。由于int类型最大值的限制,该方法最大返回Integer.MAX_VALUE,意* @Description: 味着链接迁移队列中至少存在Integer.MAX_VALUE数量的等待中的移除/拿取操作。* @Description: 逻辑:遍历整个链接迁移队列,查找未匹配的请求节点,如果未查找到或者查找到了未匹配的数据节点,则说明不存在相应操作;否* @Description: 则在在遍历过程中记录未匹配的请求节点的总数并返回。如果在遍历过程中数量已达到Integer.MAX_VALUE则提前返回。*/public int getWaitingConsumerCount() {return countOfMode(false);}/*** @Description: 名称:轮询* @Description: 作用:从链接迁移队列的队头移除元素。该方法是移除/拿取方法中"特殊值"形式的实现,当链接迁移队列存在元素时移除/拿取并返* @Description: 回元素;否则返回null。* @Description: 逻辑:~*/public E poll() {return xfer(null, false, NOW, 0);}/*** @Description: 名称:拿取* @Description: 作用:从链接迁移队列的队头移除元素。该方法是移除/拿取方法中"阻塞"形式的实现,当链接迁移队列存在元素时移除/拿取并返* @Description: 回元素;否则进行阻塞,直至有放置者或迁移者到来移除/拿取并返回元素。* @Description: 逻辑:~*/public E take() throws InterruptedException {// 获取元素。E e = xfer(null, false, SYNC, 0);if (e != null)return e;// 理论上获取到的元素必然是不为null的,但是如果操作被取消了则还是会为null的,因此需要将线程的中断状态清除。Thread.interrupted();throw new InterruptedException();}/*** @Description: 名称:拿取* @Description: 作用:从链接迁移队列的队头移除元素。该方法是移除/拿取方法中"超时"形式的实现,当链接迁移队列存在元素时移除/拿取并返回* @Description: 元素;否则阻塞指定的等待时间。如果在指定等待时间消逝前有放置者或迁移者到来则移除/拿取并返回元素;否则返回null。* @Description: 逻辑:~*/public E poll(long timeout, TimeUnit unit) throws InterruptedException {E e = xfer(null, false, TIMED, unit.toNanos(timeout));if (e != null || !Thread.interrupted())return e;throw new InterruptedException();}/*** @Description: 名称:窥视* @Description: 作用:从链接迁移队列的头部获取元素。该方法是检查方法中"特殊值"形式的实现,当链接迁移队列存在元素时返回元素;否则返回* @Description: null。* @Description: 逻辑:~*/public E peek() {return firstDataItem();}/*** @throws NullPointerException     {@inheritDoc} 空指针异常* @throws IllegalArgumentException {@inheritDoc} 非法参数异常* @Description: 名称:流失* @Description: 作用:将链接迁移队列中所有的元素都迁移到指定集中,并返回迁移元素的数量。* @Description: 逻辑:通过循环调用poll()方法反复获取链接迁移队列中元素并添加至指定集中,直至获取不到元素为止。*/public int drainTo(Collection<? super E> c) {if (c == null)throw new NullPointerException();if (c == this)throw new IllegalArgumentException();// 通过poll方法自己哦几口额int n = 0;for (E e; (e = poll()) != null; ) {c.add(e);++n;}return n;}/*** @throws NullPointerException     {@inheritDoc} 空指针异常* @throws IllegalArgumentException {@inheritDoc} 非法参数异常* @Description: 名称:流失* @Description: 作用:将链接迁移队列中最多指定数量的元素迁移到指定集中,并返回迁移元素的数量。* @Description: 逻辑:通过循环调用poll()方法反复获取链接迁移队列中元素并添加至指定集中,直至获取不到元素为止或达到指定数量为止。*/public int drainTo(Collection<? super E> c, int maxElements) {if (c == null)throw new NullPointerException();if (c == this)throw new IllegalArgumentException();int n = 0;for (E e; n < maxElements && (e = poll()) != null; ) {c.add(e);++n;}return n;}/*** Returns the number of elements in this queue.  If this queue contains more than {@code Integer.MAX_VALUE} elements, returns* {@code Integer.MAX_VALUE}.* 返回队列中的元素数量。如果队列包含超过Integer.MAX_VALUE个元素,返回Integer.MAX_VALUE。* <p>* Beware that, unlike in most collections, this method is <em>NOT</em> a constant-time operation. Because of the asynchronous nature of these* queues, determining the current number of elements requires an O(n) traversal.* 当心:不像大多数集,该方法不是一个常量时间操作(即不是一个时间复杂度为O(1)的操作)。因为这些队列的异步特性,确定元素的当前数量需* 要一次时间复杂度为O(n)遍历(即完整遍历)。** @return the number of elements in this queue 队列中元素的数量* @Description: 名称:大小* @Description: 作用:获取链接迁移队列中元素数量的近似值。* @Description: 逻辑:由于没有一个专属的字段用于保存元素数量的值,因此链接迁移队列的size()方法并不是一个时间复杂度为O(1)的操作。为了* @Description: 获取元素,其会遍历整个链接迁移队列以进行统计,因此其时间复杂度为O(n)。并且由于在此过程中存在并发操作,因此的得到的* @Description: 元素总数是不可靠的。*/public int size() {return countOfMode(true);}/*** Always returns {@code Integer.MAX_VALUE} because a {@code LinkedTransferQueue} is not capacity constrained.* 永远返回Integer.MAX_VALUE,因为链接迁移队列没有容量限制。** @return {@code Integer.MAX_VALUE} (as specified by {@link BlockingQueue#remainingCapacity() Integer.MAX_VALUE* BlockingQueue.remainingCapacity})* @Description: 名称:剩余容量* @Description: 作用:获取链接迁移队列的剩余容量。由于链接迁移队列是无界队列,因此该方法永远返回Integer.MAX_VALUE来表示无限。* @Description: 逻辑:~*/public int remainingCapacity() {return Integer.MAX_VALUE;}/*** Returns {@code true} if this queue contains no elements.* 如果队列不包含任何元素则返回true。** @return {@code true} if this queue contains no elements 如果队列不包含任何元素则返回true* @Description: 名称:是否为空* @Description: 作用:判断链接迁移队列是否为空,是则返回true;否则返回false。* @Description: 逻辑:从头开始遍历链接迁移队列,如果没有发现未匹配的数据节点或发现了未匹配的请求节点则说明不存在元素。*/public boolean isEmpty() {for (Node p = head; p != null; p = succ(p)) {if (!p.isMatched())return !p.isData;}return true;}/*** Removes a single instance of the specified element from this queue, if it is present.  More formally, removes an element {@code e} such that {@code* o.equals(e)}, if this queue contains one or more such elements. Returns {@code true} if this queue contained the specified element (or equivalently,* if this queue changed as a result of the call).* 如果存在,从队列中移除一个指定元素的实例。更正式的,如果队列包含一个或者更多如此元素,则应该使用o.equals(e)方法判断并移除一个元素。* 如果队列包含指定元素(或等效地,如果调用的结果是队列发生改变)则返回true。** @param o element to be removed from this queue, if present 如果存在,从队列中移除的元素* @return {@code true} if this queue changed as a result of the call 如果调用的节点是队列发生改变* @Description: 名称:移除* @Description: 作用:从链接迁移队列中移除指定元素,移除成功则返回true;否则返回false。* @Description: 逻辑:遍历链接迁移队列,找到指定元素并执行内部移除。内部移除可能会导致遗留,因此该方法可能会触发选票的累积与全清理。*/public boolean remove(Object o) {return findAndRemove(o);}/*** Returns {@code true} if this queue contains the specified element. More formally, returns {@code true} if and only if this queue contains at least one* element {@code e} such that {@code o.equals(e)}.* 如果队列包含指定元素则返回true。更正式地,当且仅当队列使用o.equals(e)方法判断包含至少一个元素时true。** @param o object to be checked for containment in this queue 检查队列中包含的队列* @return {@code true} if this queue contains the specified element 如果队列包含指定元素则并返回true* @Description: 名称:包含* @Description: 作用:判断链接迁移队列是否包含指定元素,是则返回true;否则返回false。* @Description: 逻辑:~*/public boolean contains(Object o) {if (o == null) return false;for (Node p = head; p != null; p = succ(p)) {Object item = p.item;if (p.isData) {if (item != null && item != p && o.equals(item))return true;} else if (item == null)break;}return false;}/*** Returns an iterator over the elements in this queue in proper sequence. The elements will be returned in order from first (head) to last (tail).* 返回一个按合适顺序遍历元素的迭代器。元素将按从头到尾的顺序返回。* <p>* The returned iterator is <a href="package-summary.html#Weakly"><i>weakly consistent</i></a>.* 返回的迭代器是弱一致性的。** @return an iterator over the elements in this queue in proper sequence  一个按合适顺序遍历元素的迭代器* @Description: 名称:迭代器* @Description: 作用:获取一个链接迁移队列的迭代器。* @Description: 逻辑:~*/public Iterator<E> iterator() {return new Itr();}/*** @Description: 名称:迭代器类* @Description: 作用:该类的对象用于迭代链接迁移队列。* @Description: 逻辑:~*/final class Itr implements Iterator<E> {/*** next node to return item for* 下个返回的项所在的节点** @Description: 名称:下个节点* @Description: 作用:持有下次迭代的节点对象。* @Description: 逻辑:~*/private Node nextNode;/*** the corresponding item* 相应的项/元素** @Description: 名称:下个元素* @Description: 作用:持有下次迭代的节点对象的项/元素。* @Description: 逻辑:~*/private E nextItem;/*** last returned node, to support remove* 上个返回的节点,以支持移除** @Description: 名称:上个节点* @Description: 作用:持有上次迭代的节点对象,以支持迭代移除操作。* @Description: 逻辑:~*/private Node lastRet;/*** predecessor to unlink lastRet* 上个节点的前驱节点** @Description: 名称:上个前驱节点* @Description: 作用:持有上个节点的前驱节点对象,以支持迭代移除操作。* @Description: 逻辑:~*/private Node lastPred;/*** Moves to next node after prev, or first node if prev null.* 移动至prev的下个节点,如果prev为null则移动至首个节点。** @Description: 名称:~* @Description: 作用:创建一个链接迁移队列的迭代器* @Description: 逻辑:实例化迭代器的同时定位相应的索引。*/Itr() {// 定位初始的上个前驱节点、上个节点及下个节点。advance(null);}/*** Moves to next node after prev, or first node if prev null.* 移动至prev的下个节点,如果prev为null则移动至首个节点。** @Description: 名称:前进* @Description: 作用:定位新的下个/上个节点,以达到迭代的效果。* @Description: 逻辑:先通过上个节点来确定上个前驱节点。随后通过指定节点向后遍历找到首个未匹配/取消的数据节点作为下个节点。在遍* @Description: 历的过程中,如果发现了已取消但是没有内部移除的节点,则进行辅助性质的内部移除。*/private void advance(Node prev) {/** To track and avoid buildup of deleted nodes in the face of calls to both Queue.remove and Itr.remove, we must include variants of unsplice* and sweep upon each advance: Upon Itr.remove, we may need to catch up links from lastPred, and upon other removes, we might need to* skip ahead from stale nodes and unsplice deleted ones found while advancing.* 为了在调用内部移除和迭代移除中追踪和避免形成已删除节点,我们必须在每次前进后兼容断开拼接和清理。迭代移除后,我们可能需要从上* 个前驱节点获取链接,并且在其它移除之后,我们可能需要跳过过期节点并断开在前进期间(即advance(Node prev)方法执行期间)发现的已* 删除节点。*/// reset lastPred upon possible deletion of lastRet// 在上个节点的可能删除后重设置上个前驱节点Node r, b;if ((r = lastRet) != null && !r.isMatched())// next lastPred is old lastRet// 下个上个前驱节点是旧的上个节点// 获取上个节点(快照)并在上个节点(快照)不为null的情况下判断其是否匹配/取消。如果上个节点(快照)没有被匹配/取消,则将至// 设置为上个前驱节点。因为如果节点已经匹配,则从逻辑上来说其已经从队列中移除了。并且在实际的实现中,其很有可能会因为出队而// 被设置为自引用,如此节点是无法用于支持迭代移除的。因此如果上个节点(快照)存在且未匹配,在前进时设置为上个前驱节点。lastPred = r;else if ((b = lastPred) == null || b.isMatched())// at start of list// 在列表的开始// 如果上个节点(快照)不满足上述条件(即不存在或已匹配/取消),则无法在前进时将之设置为上个前驱节点。因为从逻辑上来说,上个// 节点(快照)已经不存在与队列中了。而从实际实现的角度,即使其依然存在于队列中,也随时可能被移除,因此不可以将之设置为上个// 前驱节点。// 获取上个前驱节点(快照),如果上个前驱节点(快照)为null 或已经匹配/取消,则将上个前驱节点赋值为null。因为上个前驱节点(快照)// 不存在或已经匹配/取消,因此从逻辑上其已经不存在于队列中了,并且由于无法将上个节点(快照)设置为上个前驱节点(快照),因此在// 前进中对于此种情况就要将上个前驱节点(快照)赋值为null。lastPred = null;else {// help with removal of lastPred.next// 帮助移除上个前驱节点(快照)的后继节点。// 当前情况表示上个前驱节点(快照)存在并且未匹配/取消,而上个节点(快照)不存在或已匹配/取消。这说明上个节点(快照)一定发生了// 取消的情况(因为如果是匹配的话,上个前驱节点(快照)一定也会被匹配)。因此就要进行一次帮助性的节点内部移除,直至所有的取消节// 点都被内部移除为止。上个前驱节点(快照)的值不会被更新(如果其一直没有被匹配),会被保留辅助下次移除。Node s, n;while ((s = b.next) != null && s != b && s.isMatched() && (n = s.next) != null && n != s)b.casNext(s, n);}// 将上个节点赋值为指定节点。this.lastRet = prev;// 从指定节点的位置向后遍历,执行前进。for (Node p = prev, s, n; ; ) {// 如果指定节点为null,则从头节点开始遍历,否则从指定节点的后继节点开始遍历。s = (p == null) ? head : p.next;if (s == null)// 如果后继节点为null,说明已经没有可前进的节点了,直接结束。break;else if (s == p) {// 如果当前节点已经移除/拿取,则重新从头节点开始继续向后遍历。p = null;continue;}// 如果遍历到的节点即不为null也没有出队,则判断该节点是否是数据节点。如果是数据节点并且没有被匹配或取消,则该节点就会被作为下个节// 点用作迭代。Object item = s.item;if (s.isData) {if (item != null && item != s) {nextItem = LinkedTransferQueue.<E>cast(item);nextNode = s;return;}} else if (item == null)// 如果遍历到的节点是请求节点,并且没有被匹配,这就说明队列中不存在未匹配的数据节点,因此也就没有可迭代的元素,直接结束前进过程。break;// assert s.isMatched();// 断言节点已匹配/取消// 对于已匹配的请求节点的情况,则需要继续向后便利,直至找到第一个未匹配的请求或数据节点为止。if (p == null)// 继续向后遍历。p = s;else if ((n = s.next) == null)// 如果当前节点的后继节点为null,则直接结束,因为已经没有可前进节点。break;else if (s == n)// 当前节点已经出队,则从头开始重新前进。p = null;else// 如果p是正常节点,而s已被匹配/取消,则帮助断开节点的拼接,p.casNext(s, n);}// 表示没有可迭代的节点/项。nextNode = null;nextItem = null;}/*** @Description: 名称:存在下个* @Description: 作用:判断是否存在下个迭代元素。* @Description: 逻辑:判断下个节点字段是否为null。*/public final boolean hasNext() {return nextNode != null;}/*** @Description: 名称:下个* @Description: 作用:获取下个迭代的元素。* @Description: 逻辑:获取下个元素用于返回,并以下个节点为起点向后遍历,找到新的下个元素。*/public final E next() {Node p = nextNode;if (p == null) throw new NoSuchElementException();E e = nextItem;advance(p);return e;}/*** @Description: 名称:移除* @Description: 作用:移除上个迭代的元素。* @Description: 逻辑:通过上个前驱节点将上个节点从链接迁移队列中内部移除。内部移除前先将上个节点强制匹配,已避免在内部移除的过* @Description: 程中匹配。*/public final void remove() {final Node lastRet = this.lastRet;if (lastRet == null)throw new IllegalStateException();this.lastRet = null;// 强制将上个节点匹配,避免其在断开拼接的过程中if (lastRet.tryMatchData())unsplice(lastPred, lastRet);}}/*** A customized variant of Spliterators.IteratorSpliterator*/static final class LTQSpliterator<E> implements Spliterator<E> {static final int MAX_BATCH = 1 << 25;  // max batch array size;final LinkedTransferQueue<E> queue;Node current;    // current node; null until initializedint batch;          // batch size for splitsboolean exhausted;  // true when no more nodesLTQSpliterator(LinkedTransferQueue<E> queue) {this.queue = queue;}public Spliterator<E> trySplit() {Node p;final LinkedTransferQueue<E> q = this.queue;int b = batch;int n = (b <= 0) ? 1 : (b >= MAX_BATCH) ? MAX_BATCH : b + 1;if (!exhausted &&((p = current) != null || (p = q.firstDataNode()) != null) &&p.next != null) {Object[] a = new Object[n];int i = 0;do {Object e = p.item;if (e != p && (a[i] = e) != null)++i;if (p == (p = p.next))p = q.firstDataNode();} while (p != null && i < n && p.isData);if ((current = p) == null)exhausted = true;if (i > 0) {batch = i;return Spliterators.spliterator(a, 0, i, Spliterator.ORDERED | Spliterator.NONNULL |Spliterator.CONCURRENT);}}return null;}@SuppressWarnings("unchecked")public void forEachRemaining(Consumer<? super E> action) {Node p;if (action == null) throw new NullPointerException();final LinkedTransferQueue<E> q = this.queue;if (!exhausted &&((p = current) != null || (p = q.firstDataNode()) != null)) {exhausted = true;do {Object e = p.item;if (e != null && e != p)action.accept((E) e);if (p == (p = p.next))p = q.firstDataNode();} while (p != null && p.isData);}}@SuppressWarnings("unchecked")public boolean tryAdvance(Consumer<? super E> action) {Node p;if (action == null) throw new NullPointerException();final LinkedTransferQueue<E> q = this.queue;if (!exhausted &&((p = current) != null || (p = q.firstDataNode()) != null)) {Object e;do {if ((e = p.item) == p)e = null;if (p == (p = p.next))p = q.firstDataNode();} while (e == null && p != null && p.isData);if ((current = p) == null)exhausted = true;if (e != null) {action.accept((E) e);return true;}}return false;}public long estimateSize() {return Long.MAX_VALUE;}public int characteristics() {return Spliterator.ORDERED | Spliterator.NONNULL |Spliterator.CONCURRENT;}}/*** Returns a {@link Spliterator} over the elements in this queue.** <p>The returned spliterator is* <a href="package-summary.html#Weakly"><i>weakly consistent</i></a>.** <p>The {@code Spliterator} reports {@link Spliterator#CONCURRENT},* {@link Spliterator#ORDERED}, and {@link Spliterator#NONNULL}.** @return a {@code Spliterator} over the elements in this queue* @implNote The {@code Spliterator} implements {@code trySplit} to permit limited* parallelism.* @since 1.8*/public Spliterator<E> spliterator() {return new LTQSpliterator<E>(this);}/*** Saves this queue to a stream (that is, serializes it).** @param s the stream* @throws IOException if an I/O error occurs* @serialData All of the elements (each an {@code E}) in* the proper order, followed by a null*/private void writeObject(ObjectOutputStream s) throws IOException {s.defaultWriteObject();for (E e : this)s.writeObject(e);// Use trailing null as sentinels.writeObject(null);}/*** Reconstitutes this queue from a stream (that is, deserializes it).** @param s the stream* @throws ClassNotFoundException if the class of a serialized object*                                could not be found* @throws IOException            if an I/O error occurs*/private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {s.defaultReadObject();for (; ; ) {@SuppressWarnings("unchecked")E item = (E) s.readObject();if (item == null)break;elseoffer(item);}}// Unsafe mechanicsprivate static final sun.misc.Unsafe UNSAFE;private static final long headOffset;private static final long tailOffset;private static final long sweepVotesOffset;static {try {UNSAFE = sun.misc.Unsafe.getUnsafe();Class<?> k = LinkedTransferQueue.class;headOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("head"));tailOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("tail"));sweepVotesOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("sweepVotes"));} catch (Exception e) {throw new Error(e);}}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/4870.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

vue3相对vue2有哪些改变?

https://blog.csdn.net/weixin_44475093/article/details/112386778 https://blog.csdn.net/userDengDeng/article/details/114941956 一、vue3的新特性&#xff1a; 1、速度更快 vue3相比vue2 重写了虚拟Dom实现编译模板的优化更高效的组件初始化undate性能提高1.3~2倍SSR速度…

数据库概论实验一

声明&#xff1a;著作权归作者所有。商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处。 本文章对数据库概论实验一_求出一箱(每箱装100个)零件的重量 并将输出结果-CSDN博客文章浏览阅读2.7k次&#xff0c;点赞4次&#xff0c;收藏25次。实验数据库&#xff0c;表…

绿色能源发展关键:优化风电运维体系

根据QYResearch调研团队最新发布的《全球风电运维市场报告2023-2029》显示&#xff0c;预计到2029年&#xff0c;全球风电运维市场的规模将攀升至307.8亿美元&#xff0c;并且在接下来的几年里&#xff0c;其年复合增长率&#xff08;CAGR&#xff09;将达到12.5%。 上述图表及…

gerrit 搭建遇到的问题

1、启动Apache&#xff0c;端口被占用 : AH00072: make sock: could not bind to address (0S 10048)通常每个套接字地址(协议/网络地址/端口)只允许使用一次。: AH00072: make sock: could not bind to address 0.0.0.:443 a AH00451: no listening sockets available, shutti…

栈和队列相关题 , 用队列实现栈, 用栈实现队列 ,设计循环队列 C/C++双版本

文章目录 1.用队列实现栈2.用栈实现队列3. 设计循环队列 1.用队列实现栈 225. 用队列实现栈 思路&#xff1a; 使用两个队列&#xff0c;始终保持一个队列为空。 当我们需要进行压栈操作时&#xff0c;将数据压入不为空的队列中&#xff08;若两个都为空&#xff0c;则随便压…

关于STM32在代码中的而GPIO里面的寄存器(ODR等)不需要宏定义的问题

1.GPIO为什么需要宏定义地址 在 STM32 这样的微控制器中&#xff0c;硬件寄存器的地址是固定的并且特定于每个外设&#xff08;比如 GPIOA、GPIOB 等&#xff09;。为了方便代码访问这些硬件寄存器&#xff0c;我们通常会使用宏定义来指定每个外设的基地址。这样做有几个理由&a…

kimi智能助手,5大高阶玩法,95%的人还不知道

01 智能搜索:精准定位,一键获取最佳答案 Kimi,作为您的AI助手,拥有卓越的网络搜索能力。 我们能够迅速穿梭于信息海洋,为您筛选出五篇精选网络文章,并提供直接的网址链接。 Kimi的总结能力同样出色,特别适合那些追求效率、不愿深陷长篇文章的用户。 02 PDF速读:快速把…

关于数学建模的一些介绍

为了更好了解世界&#xff0c;我们可以通过数学来描述许多特定的现象&#xff0c;而数学模型就是现实世界的理想化&#xff0c;不过它永远不能完全精确地表示现实世界。 在这篇文章中&#xff0c;我将介绍一些数学建模的基本概念以及相应的基础知识&#xff0c;而关于更具体的…

远翔升压恒流芯片FP7209X与FP7209M什么区别?做以下应用市场摄影补光灯、便携灯、智能家居(调光)市场、太阳能、车灯、洗墙灯、舞台灯必看!

一&#xff0c;概述 FP7209是台湾远翔一款非同步升压LED驱动IC&#xff0c;封装有2种&#xff0c;分别是SOP-8L(EP), TSSOP-14L(EP)。控制外部开关NMOS。 输入低启动电压2.8V&#xff0c;可支持单节锂电池供电。工作电压5V&#xff0c;VFB反馈电压0.25V&#xff0c;反馈电压低…

算法日记 18 day 二叉树

最后三题&#xff0c;二叉树就结束啦&#xff01;&#xff01;&#xff01; 题目&#xff1a;修剪二叉搜索树 669. 修剪二叉搜索树 - 力扣&#xff08;LeetCode&#xff09; 给你二叉搜索树的根节点 root &#xff0c;同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树…

hashcat使用

0.介绍 Hashcat 软件是一款非常强大的、开源的、号称世界上最快的密码破解软件&#xff0c;配合强大的字典&#xff0c;可以破译超过百分之九十的密码。Hashcat 目前支持各类公开算法高达240类&#xff0c;市面上公开的密码加密算法基本都支持&#xff0c;有 Microsoft LM 哈希…

mysql 安装 windows

新版安装 新版本安装 如果出现initializing database无法安装 则用我当前版本传送门 如MySQL 安装时没有developer default 选项 解决方法传送门 如果上述还不行 可以选择full 汉化下载 传送门

基于Redis缓存机制实现高并发接口调试

创建接口 这里使用的是阿里云提供的接口服务直接做的测试&#xff0c;接口地址 curl http://localhost:8080/initData?tokenAppWithRedis 这里主要通过参数cacheFirstfalse和true来区分是否走缓存&#xff0c;正常的业务机制可能是通过后台代码逻辑自行控制的&#xff0c;这…

vue常见题型(1-10)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 2.2双向绑定的原理是什么vue框架采用的是数据双向绑定的方式&#xff0c;由三个重要部分构成2.2.1.ViewModel2.2.2 双向绑定2.2.3.1.编译Compile2.2.3.2.依赖收集 3…

C语言变量与强制类型转换深度解析

在上一篇文章中&#xff0c;小编对数据类型进行了详细的讲解与剖析&#xff0c;所以本篇文章小编要带大家理解变量和强制类型转。还是老规矩&#xff0c;来波鸡汤&#xff0c;学习一定不能着急&#xff0c;无法一下就学明白的知识我们需要给他时间&#xff0c;一定不要在一个知…

JAVA+微信小程序前后端源码 微信OCR识别 识别身份证信息

官方文档:身份证识别 | 微信开放文档 实现效果 : 用的奥巴马的网络图片测试,图片 后端JAVA代码 这里用的若依的后端,前后端分离版的 package com.ruoyi.common.utils;import java.io.File; import java.io.IOException;import org.apache.http.HttpEntity; import org.apac…

SL6605 输入0.8-5.5V 单颗锂电池驱动LED升压恒流限流方案

一、芯片特性 输入电压范围广&#xff1a;SL6605可接受0.8V至5.5V的输入电压&#xff0c;使其能够轻松应对各种锂电池电压波动。升压恒流功能&#xff1a;该芯片具有升压能力&#xff0c;可将低电压输入转换为适合LED驱动的高电压&#xff0c;并保持恒定的输出电流。限流保护&…

ubuntu 安装go和vscode

1 安装Go 打开终端&#xff0c;执行以下命令下载Golang安装包&#xff1a; wget https://golang.org/dl/go1.xx.x.linux-amd64.tar.gz注意&#xff1a;替换命令中的“1.xx.x”为最新版本号&#xff0c;例如&#xff1a;1.23.2. 2. 解压安装包&#xff1a; sudo tar -C /usr/…

[spring源码]spring启动流程

spring启动流程 AnnotationConfigApplicationContext的构造方法 1.父类构造方法&#xff0c;构造一个DefaultListableBeanFactory 在调用AnnotationConfigApplicationContext的构造方法之前&#xff0c;会调用父类GenericApplicationContext的无参构造方法&#xff0c;会构造…

Kafka自动生产消息软件(自动化测试Kafka)

点击下载《Kafka服务端(含Zookeeper)一键自启软件》 点击下载《kafka客户端生产者消费者kafka可视化工具&#xff08;可生产和消费消息&#xff09;》 点击下载《Kafka自动生产消息软件》 1. 前言 在软件开发过程中&#xff0c;Kafka常被用作消息队列来处理特定的业务功能。为…