fail-safe迭代器基于创建时的独立快照副本遍历,不抛concurrentmodificationexception;典型实现有copyonwritearraylist、copyonwritearrayset和concurrenthashmap.keyset().iterator(),但后者为弱一致性。

Java 迭代器的“安全失败”(Fail-Safe)不是指它不会出错,而是指它**在检测到并发修改时不会崩溃或抛异常**——它的应对方式是:不依赖原集合实时状态,而是基于创建那一刻的**独立快照副本**完成遍历。
核心是“快照 + 读写分离”
Fail-Safe 迭代器不访问原始集合对象,而是在调用 iterator() 的瞬间,生成一份只读的、不可变的状态副本(如数组引用或结构切片)。后续所有 hasNext() 和 next() 操作都只在这个副本上进行。
- 原集合的增删改操作(哪怕发生在同一秒),对当前迭代器完全透明
- 多个迭代器之间互不影响,每个都持有一份独立快照
- 快照不是懒加载,也不是共享引用,而是创建即确定、不可更新的视图
典型实现类的行为特点
真正符合 Fail-Safe 定义的集合集中在 java.util.concurrent 包中:
- CopyOnWriteArrayList:每次 iterator() 返回当前数组引用;每次写操作(add/remove)都新建数组并原子更新内部引用。迭代器永远看到旧数组
- CopyOnWriteArraySet:底层基于 CopyOnWriteArrayList,行为一致
- ConcurrentHashMap.keySet().iterator():不复制全量数据,但按桶分段遍历,提供弱一致性(Weakly Consistent)——可能漏掉刚插入的元素,也可能看到部分迁移中的节点,但绝不抛 ConcurrentModificationException
它“安全”的代价是什么
Fail-Safe 的可靠性来自明确取舍:
- 不反映实时变更:迭代过程中新增的元素不会出现,已删除的元素仍可能被遍历到
- 内存开销真实存在:尤其是大集合频繁创建迭代器时,会触发多次堆内存拷贝
- 写操作成本高:CopyOnWrite 系列的 add/remove 需要复制整个数组,不适合写密集场景
- 适合读多写少:监听器列表、配置缓存、广播事件等场景天然匹配
和 Fail-Fast 的关键区别
别把包路径当万能判断标准,要看实际行为逻辑:
- Fail-Fast(如 ArrayList、HashMap):用 modCount 校验版本号,一旦不匹配立即抛 ConcurrentModificationException,不管单线程还是多线程
- Fail-Safe(如 CopyOnWriteArrayList):根本没 modCount 校验机制,它压根不关心原集合有没有变——因为它早就不看了
- 注意例外:ConcurrentHashMap 的迭代器不是严格 Fail-Safe,JavaDoc 明确称其为 weakly consistent,不保证快照完整性,但同样不抛异常
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











