ConcurrentLinkedQueue 的迭代器是基于弱一致性快照的 fail-safe 迭代器,不复制数据,构造时尽力查找首个非空节点作为起点,遍历时跳过 item 为 null 的节点,不抛 ConcurrentModificationException,但可能漏掉新元素或包含已逻辑删除的滞留节点。

ConcurrentLinkedQueue 的迭代器是 fail-safe(安全失败) 类型,但它和 CopyOnWriteArrayList 的 fail-safe 有本质区别——它不复制数据,而是基于弱一致性快照实现的遍历。当队列在被迭代的同时频繁执行 poll(),Iterator 的指针移动行为既不是实时跟踪 head,也不是完全冻结状态,而是在构造时“尽力捕获一个可用起点”,之后按链表 next 指针逐步推进。
迭代器的起点不是实时 head,而是“逻辑头节点”
- 创建
iterator()时,并不直接锁定或复制整个链表; - 它会尝试从当前
head字段出发,跳过已被poll()标记为已移除(item = null)的节点,找到第一个item != null的有效节点作为遍历起点; - 这个过程可能因并发
poll()正在修改 head 或中间节点而“错过”刚被移除的头部元素,也可能“多包含”尚未被 head 更新覆盖的旧节点。
遍历时指针只靠 next 向后移动,不回溯、不重试
- Iterator 内部维护一个当前节点引用(初始为上面找到的有效头);
- 每次调用
next(),就将该引用更新为current.next,再返回current.item; -
不会重新检查 head 是否已变,也不会跳过中途被 poll() 清空的节点 —— 如果某个节点在遍历途中被其他线程
poll()并设为item = null,而该节点尚未被 iterator 访问,那么 iterator 仍会在后续访问到它(返回 null?不,它会跳过 item == null 的节点); - 实际上,iterator 在
next()中会对当前节点做item != null判断;若为 null,则沿next继续找,直到找到非 null 或链表结束。
频繁 poll 对遍历结果的典型影响
- ✅ 不会抛
ConcurrentModificationException:这是 fail-safe 的核心体现; - ⚠️ 可能跳过刚入队、尚未被 tail 反映的新元素:因为 iterator 不读 tail,只从 head 出发顺链表走;
- ⚠️ 可能包含已被 poll() 移除但 head 尚未更新到位的“滞留节点”:例如某节点 item 被设为 null,next 也被修正,但 iterator 已经过该节点前驱,仍可能抵达它(然后跳过);
- ⚠️ 遍历顺序始终是 FIFO,但覆盖范围不确定:可能比当前实际队列内容略少(漏掉新插入),也可能略多(含已逻辑删除但物理未清理的节点)。
真实遍历逻辑简化示意(伪代码)
Node current = findFirstNonNullFromHead(); // 跳过 item==null 的哨兵或已删节点
while (current != null) {
E item = current.item;
if (item != null) {
return item; // 返回并 advance
}
current = current.next; // item 为 null,继续往后找
}
return null; // 遍历结束这意味着:
-
poll()操作本身不影响 iterator 当前正在处理的节点; - 但会悄悄改变后续节点的
item值或next链接,从而影响 iterator 下一步走向; - 整个过程无锁、无阻塞、无重试,性能高,代价是结果不保证反映某一时刻的精确快照。
本质上,ConcurrentLinkedQueue 的 iterator 是一种“尽力而为”的弱一致遍历器——它不承诺看到全部、也不承诺只看到活着的,只承诺看到的都是曾经合法插入过的、且未被跳过的元素,并严格保持插入顺序。


















