PriorityBlockingQueue 的迭代器是 fail-safe 的,不抛 ConcurrentModificationException,也不保证实时性、一致性和有序性,仅提供弱一致性快照视图。

PriorityBlockingQueue 的迭代器是 fail-safe 的,但它 不保证迭代过程中看到的节点调整是实时、一致或有序的。所谓“正确性”,需明确是指数据结构层面的堆性质维持,还是用户视角的遍历结果一致性——这两者在该队列中是分离的。
迭代器不参与堆调整,只读快照
PriorityBlockingQueue 的 iterator() 返回的是一个弱一致性迭代器:它不加锁,也不复制数组,而是直接访问当前内部数组 queue 的一个“瞬时视图”。这个视图可能包含部分已修改但尚未完成堆调整的中间状态,也可能跳过刚插入但尚未 siftUp 到位的元素,或仍包含已被 take() 移除但尚未从数组中清理的“逻辑删除”残留(实际通过 size 控制可见范围)。
- 迭代过程不会触发任何 siftUp 或 siftDown 操作;堆调整只发生在 offer/take/poll 等写操作内部
- 迭代器遍历的是物理数组索引 0 到 size−1 的连续段,但不保证这些元素满足堆序(例如,siftUp 可能只进行到一半就被中断)
- 因此,迭代结果既不是按优先级排序的,也不是某次操作前后的严格快照,而是“尽力而为”的近似视图
节点调整本身始终正确,与迭代无关
堆的结构性正确性由锁和原子操作保障,与迭代器完全解耦:
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
- 所有修改操作(offer/take/remove)都持有 ReentrantLock,在临界区内完成 siftUp/siftDown,确保堆性质在每次写操作结束时恢复
- 扩容、元素移动、父子关系重排等底层调整,均在加锁保护下执行,不受迭代器干扰
- 即使多个线程同时迭代 + 修改,堆结构自身不会损坏;只是迭代器看到的内容可能滞后或不完整
为什么叫 fail-safe?关键在不抛异常
这里的 “safe” 指的是安全性而非准确性:
- 迭代器永远不会因并发修改抛 ConcurrentModificationException(不像 ArrayList)
- 它不会因数组扩容、元素移动导致空指针或数组越界(内部有边界检查)
- 它允许在遍历时安全地继续运行,哪怕结果不反映某一时刻的精确状态
若需强一致性遍历,必须主动同步
如果业务要求迭代时看到稳定、有序、无遗漏的结果,不能依赖 iterator():
- 调用 toArray() 获取当前数组副本,再对副本排序(Arrays.sort(arr, comparator))
- 或在 lock 保护下手动遍历:queue.lock.lock(); try { ... } finally { queue.lock.unlock(); }
- 注意:lock 是私有字段,无法直接访问,实际应避免绕过 public API;更推荐用 toArray() + 排序
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










