PriorityQueue 的迭代器是 fail-safe 而非 fail-fast,它通过构造时复制数组快照实现弱一致性,不校验 modCount,遍历时完全基于副本,因此不会抛 ConcurrentModificationException。

Java 中 PriorityQueue 的迭代器(Itr)确实实现了 fail-fast 机制,但它**不校验 modCount** —— 这是关键点。
PriorityQueue 的 Itr 不基于 modCount 实现 fail-fast
PriorityQueue 的内部迭代器 Itr 是一个弱一致性(weakly consistent)迭代器,它不继承 AbstractIterator 或使用 modCount/expectedModCount 模式。与 ArrayList、HashMap 等容器不同,PriorityQueue 的迭代器在构造时会复制当前堆数组的快照(snapshot),后续遍历操作完全基于这个副本进行。
这意味着:
- 迭代过程中即使队列被结构性修改(如
add、poll、remove),原数组和快照已分离,不会触发ConcurrentModificationException; - 迭代器不会读取或比较
modCount,其源码中根本不存在expectedModCount字段或相关校验逻辑; - 它不保证反映迭代期间的实时修改,但也不会因并发修改而抛异常 —— 属于“fail-safe”行为,而非 “fail-fast”。
为什么 PriorityQueue 选择快照而非 modCount 校验
根本原因在于 PriorityQueue 的底层是可变长度的 Object[] 数组 + 堆调整逻辑,其结构性修改(尤其是 poll() 或 add())常伴随数组缩容、元素重排、甚至数组重建(如扩容)。若强行用 modCount 校验:
- 每次堆调整都需更新
modCount,但堆操作本身频繁且内部细节复杂(如siftDown、siftUp),难以精准界定“结构性修改”的边界; - 迭代器若持引用到原数组,遍历时可能遇到
null元素、越界或数据错乱(因堆结构动态变化),风险高于抛异常; - 快照方式更简单可靠:构造时
Arrays.copyOf(queue, size),后续只读副本,天然线程安全(针对该迭代器自身)且无并发校验开销。
对比典型 fail-fast 迭代器(如 ArrayList$Itr)
以 ArrayList 为例,其 Itr 在创建时记录 expectedModCount = modCount,每次 next() 或 remove() 前都会检查 modCount == expectedModCount,不等则抛异常。
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
而 PriorityQueue$Itr 的 next() 方法只操作本地快照数组和计数器 cursor,核心逻辑类似:
public E next() {
if (cursor >= size) throw new NoSuchElementException();
return (E) snapshot[cursor++]; // 完全不碰 queue[] 或 modCount
}没有 checkForComodification() 调用,也没有任何对 modCount 的访问。
实际开发中的注意事项
使用 PriorityQueue.iterator() 时需明确:
- 它不是 fail-fast 的,不能依赖它检测并发修改;
- 迭代结果反映的是创建迭代器那一刻的队列状态,后续增删不影响本次遍历,但也看不到新加入/移除的元素;
- 若需强一致性或实时性,应自行同步访问(如用
Collections.synchronizedQueue(new PriorityQueue())),或改用线程安全的替代品(如ConcurrentLinkedQueue,但注意它不保证优先级顺序); - 不要误以为 “有迭代器” 就等于 “支持 modCount 校验” —— 行为由具体实现决定,必须查源码或文档确认。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










