blockingqueue 的迭代器是 fail-fast 的,不支持 fail-safe;其 iterator() 方法在检测到并发修改时抛出 concurrentmodificationexception,需通过快照(如 new arraylist(queue))或改用 concurrentlinkedqueue、copyonwritearraylist 等真正 fail-safe 的集合来规避。

Java 中的 BlockingQueue 实现(如 ConcurrentLinkedQueue、CopyOnWriteArrayList 的迭代器)并不提供 fail-safe 迭代器;真正具备 fail-safe 特性的其实是 ConcurrentHashMap 或 CopyOnWriteArrayList 等并发集合的迭代器。而标准的 BlockingQueue 接口实现(如 ArrayBlockingQueue、LinkedBlockingQueue)**不支持 fail-safe 迭代器**——它们的 iterator() 方法返回的是 fail-fast 迭代器。
BlockingQueue 迭代器本质是 fail-fast
ArrayBlockingQueue 和 LinkedBlockingQueue 的 iterator() 返回的迭代器在检测到结构修改(如其他线程调用 add、remove、poll 等)时,会抛出 ConcurrentModificationException。这是因为它们内部维护了 modCount 计数器,迭代器在创建和每次操作前校验该值是否被修改。
- 迭代器不是线程安全的,也不复制数据
- 仅适用于单线程遍历,或明确保证遍历时无并发修改
- 不能用于边读边由其他线程写入的生产者-消费者场景
想实现 fail-safe 迭代,需手动快照
若需要在 BlockingQueue 上获得类似 fail-safe 的行为(即遍历时不受并发修改影响),必须显式创建当前队列内容的不可变副本:
- 调用
new ArrayList(queue)或queue.toArray()获取快照 - 对副本进行遍历,原队列可继续被其他线程增删
- 注意:快照是某一时刻的视图,不反映后续变更,也存在内存与一致性开销
示例:
BlockingQueue<string> queue = new LinkedBlockingQueue();
// ... 生产者持续入队
List<string> snapshot = new ArrayList(queue); // 安全快照
for (String s : snapshot) {
System.out.println(s); // 不会因 queue 被修改而失败
}</string></string>
替代方案:选用真正 fail-safe 的并发集合
如果业务强依赖遍历时的并发安全性,应避免直接迭代 BlockingQueue,转而使用设计上支持 fail-safe 迭代的集合:
-
ConcurrentLinkedQueue:迭代器弱一致性(weakly consistent),不抛CME,可能跳过或重复元素,但不会失败 -
CopyOnWriteArrayList:迭代器基于创建时刻的数组副本,绝对 fail-safe,适合读多写少 - 自定义封装:包装
BlockingQueue,暴露snapshotIterator()方法,内部做同步 + 复制
小结:BlockingQueue 本身不提供 fail-safe 迭代器
它的迭代器是 fail-fast 的,这是有意设计——强调“遍历期间不应有结构变更”。真正的 fail-safe 行为需靠快照、选用合适并发集合,或重构访问模式(例如用 drainTo 批量消费,而非遍历)。理解这一边界,能避免线上因 CME 导致的隐蔽故障。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











