concurrentlinkeddeque采用懒更新与可达性保障设计,head/tail非实时精确但确保常数步内可达真实首尾;通过终止符、自环、cas/lazyset及hops=2阈值机制平衡性能与正确性。

ConcurrentLinkedDeque 在处理首尾变量(head 和 tail)的存取时,并不直接让它们始终精确指向逻辑上的第一个或最后一个有效节点,而是采用“懒更新 + 可达性保障”的设计。这种策略在高并发下显著减少 volatile 写操作,同时通过 CAS 和链式遍历维持语义正确性。
head/tail 不是实时准确的首尾指针
head 和 tail 都是 volatile Node<e></e> 类型,但它们只是“快照式”引用——可能指向已逻辑删除的节点、哨兵节点,甚至中间某个非端点节点。关键在于:从 head 出发,总能沿 prev 链在常数步内(通常 ≤2 步)到达真正的 first node;从 tail 出发,总能沿 next 链在常数步内到达真正的 last node。这种“可达性保证”比“实时精确性”更重要,也更轻量。
- 初始化时,head 和 tail 都指向同一个 item 为 null 的 dummy 节点
- 入队/出队成功后,tail 或 head 可能延迟更新,只在必要时(如发现当前 tail 已非末端)才尝试 CAS 更新
- 允许 head/tail “落后”于真实首尾,但绝不允许“超前”(即不能指向尚未完成链接的节点)
首尾操作依赖终止符与自环机制
当节点被移除时,ConcurrentLinkedDeque 不立即从物理链表中摘除,而是分阶段处理。其中关键一环是引入两个静态终止节点:PREV_TERMINATOR 和 NEXT_TERMINATOR。它们用于标记链表边界,并防止 ABA 问题和遍历越界。
- first 节点出列后,其
next被设为自环(next = first),prev设为 PREV_TERMINATOR(该节点的next == itself) - last 节点出列后,其
prev设为自环,next设为 NEXT_TERMINATOR(该节点的prev == itself) - 后续遍历时,遇到 PREV_TERMINATOR 或 NEXT_TERMINATOR 即知已达边界,停止继续追溯
CAS 与 lazySet 配合控制可见性与性能
对 head/tail 的修改全部使用 CAS(如 casHead(old, new)),确保更新原子性;而对节点内部指针(如 next、prev)的写入,则区分场景:关键链接用 CAS,非关键推进用 lazySet(即 putOrderedObject)。
-
lazySetNext()和lazySetPrev()用于设置新节点的前后向链接,不强制刷新缓存,但保证后续读操作能看到(因有 volatile 读屏障配合) - item 字段用
casItem()清空(逻辑删除),用UNSAFE.putObject()初始化,均依托 volatile 语义实现线程间可见 - 所有对 head/tail 的读取都带 volatile 语义,确保看到最新写入值,哪怕它不是“最准”的
首尾变量更新遵循 HOPS 阈值策略
为避免频繁更新 head/tail 带来的开销,ConcurrentLinkedDeque 引入了 HOPS = 2 的阈值机制:只有当当前 head/tail 与真实首/尾之间“跳数”超过 2 时,才尝试推动更新。
- 例如,若从 tail 开始,需连续两次
next才能到达 last node,则触发casTail(tail, newLast) - 这既抑制了高频写,又防止 tail 过度滞后导致每次入队都要长距离遍历
- 类似逻辑也应用于 head,在 poll 操作中判断是否需要推进 head 指针










