快速失败机制是检测结构性并发修改的调试辅助机制,通过modCount与expectedModCount比对,不匹配时抛ConcurrentModificationException,单线程误改结构也会触发。

Java 多线程集合迭代时的快速失败机制,本质不是“专为多线程设计的安全保障”,而是**一种检测结构性并发修改的调试辅助机制**——它用即时抛异常的方式,帮你发现“不该这么写的代码”。
快速失败的核心逻辑是“版本对不上就停”
ArrayList、HashMap 等非线程安全集合内部有一个 modCount(修改计数器),每次 add、remove、clear 等结构性操作都会让 modCount 加 1。当你调用 iterator() 获取迭代器时,迭代器会把此刻的 modCount 值存为自己的 expectedModCount。之后每次 next() 或 remove() 前,都会检查:如果 modCount ≠ expectedModCount,立刻抛 ConcurrentModificationException。
这个校验不依赖锁,也不保证强一致性;它只在迭代器“恰好走到校验点”时起作用。所以它不能替代真正的线程安全方案,但能高效暴露典型错误。
多线程下触发快速失败的典型场景
- 线程 A 正在用 Iterator 遍历 ArrayList,线程 B 同时调用 list.add() —— 下一次 A 调用 next() 就抛异常
- 两个线程各自持有一个迭代器,都试图通过 list.remove() 删除元素 —— 其中一个大概率因 modCount 不匹配而失败
- 主线程遍历,子线程清空集合(list.clear())—— 遍历中断,异常抛出
为什么单线程也会触发?
快速失败不区分线程数,只看“谁改了结构”。比如单线程中:
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
立即学习“Java免费学习笔记(深入)”;
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String s = it.next();
list.add("new"); // ❌ 错误:用集合方法改结构
}
第一次 next() 后 modCount 已变,第二次 next() 前校验失败,照样抛异常。这说明:它的真正约束是迭代期间禁止用集合自身方法修改结构,而非单纯防多线程。
合法绕过方式(不推荐滥用)
- 用迭代器自身的 remove() 方法:it.remove() 会同步更新 expectedModCount,不会触发异常
- 改用 fail-safe 集合:如 CopyOnWriteArrayList,迭代基于快照,修改不影响遍历
- 加显式同步:synchronized 包裹整个遍历+修改块(注意性能和死锁风险)
- 先收集待操作元素,遍历结束后统一处理(如先记下要删的索引,再倒序删除)
快速失败不是 bug,是设计者给你留的提示条:这里存在潜在的数据竞争或逻辑错误,该重构了。

















