Fail-Fast在并发修改时立即抛ConcurrentModificationException,依赖modCount校验;Fail-Safe通过快照或线程安全结构避免异常,如CopyOnWriteArrayList和ConcurrentHashMap。

Java集合框架中,Fail-Fast 和 Fail-Safe 是两种截然不同的迭代器容错机制,核心区别在于:前者在检测到并发修改时立即抛出 ConcurrentModificationException,后者则通过复制数据或使用线程安全结构避免该异常,允许并发读写。
Fail-Fast:快速失败,依赖 modCount 检查
大多数 JDK 原生非线程安全集合(如 ArrayList、HashMap、HashSet)采用 Fail-Fast 机制。其原理是在集合内部维护一个修改计数器 modCount,每次结构性修改(增、删、清空)都会递增;迭代器初始化时记录该值为 expectedModCount,每次调用 next() 或 remove() 前校验两者是否一致。
- 若不一致(例如遍历时另一个线程或同一线程的其他代码修改了集合),立刻抛出
ConcurrentModificationException - 单线程下也可能触发:比如用
for-each遍历ArrayList时直接调用list.remove(),而非迭代器自身的remove() - 注意:该检查不是 100% 实时(仅在迭代关键节点校验),也不能保证多线程绝对安全——它只是“尽力而为”的检测机制
Fail-Safe:安全失败,基于快照或并发结构
Fail-Safe 迭代器不依赖原集合的实时状态,而是访问其某一时刻的副本或专门设计的线程安全结构,因此不会因外部修改而抛异常。
-
java.util.concurrent包中的集合(如CopyOnWriteArrayList、ConcurrentHashMap)是典型代表 -
CopyOnWriteArrayList的迭代器基于创建时的数组快照,后续所有写操作都在新数组上进行,旧迭代器不受影响 -
ConcurrentHashMap迭代器可容忍部分更新(如桶级锁 + 分段遍历),不抛ConcurrentModificationException,但不保证反映最新全部修改(弱一致性)
如何选择与规避问题
没有绝对优劣,需按场景权衡:
立即学习“Java免费学习笔记(深入)”;
- 读多写少且需强一致性 → 用
CopyOnWriteArrayList(适合监听器列表等) - 高并发读写、接受弱一致性 → 选
ConcurrentHashMap或ConcurrentLinkedQueue - 单线程遍历中要删除元素 → 用迭代器
remove()方法,而非集合自身remove() - 多线程共享普通集合 → 必须加锁(如
synchronized或Collections.synchronizedList()),但注意后者仅方法同步,遍历仍需手动同步块
一个小验证示例
以下代码会抛 ConcurrentModificationException:
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
for (String s : list) {
if ("b".equals(s)) list.remove(s); // ❌ 触发 Fail-Fast
}
改为安全写法:
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String s = it.next();
if ("b".equals(s)) it.remove(); // ✅ 使用迭代器 remove()
}









