fail-fast是java集合框架中一种有意设计的错误检测策略,通过modcount与expectedmodcount比对,在迭代中检测到非迭代器方式的结构性修改(如add、remove)时立即抛出concurrentmodificationexception,以暴露逻辑错误;它不保证线程安全,仅作开发期调试辅助。

Java 集合框架中的 Fail-Fast 机制不是“bug”,而是有意设计的错误检测策略:只要在迭代过程中,集合结构被非迭代器方式修改,就立刻抛出 ConcurrentModificationException,强制暴露逻辑错误。
为什么一改就报 ConcurrentModificationException?
根本原因在于两个计数器不匹配——modCount 和 expectedModCount。
集合(如 ArrayList)内部维护一个 modCount,每次调用 add()、remove()、clear() 等结构性操作时,它就 +1;而迭代器创建时,会把当时的 modCount 值存为自己的 expectedModCount。后续每次调用 next() 或 remove() 前,都会检查这两个值是否还相等。
- 相等 → 继续遍历
- 不相等 → 说明集合被意外改过 → 立即抛异常
哪些操作会触发 Fail-Fast?
关键看“谁动了结构”和“怎么动的”。以下常见场景都会触发:
- 增强 for 循环中直接调用
list.remove(x) - 普通 for 循环中边遍历边用
list.add()或list.remove() - 多线程环境下,一个线程在遍历,另一个线程在增删元素
- 迭代器创建后,手动调用集合的
clear()或ensureCapacity()等影响结构的方法
注意:list.set(index, x) 不会触发——它不改变集合大小或结构,只替换值,modCount 不变。
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
为什么迭代器自己的 remove() 不报错?
因为迭代器的 remove() 方法是“受信任”的操作:
- 它内部会同步更新自己的
expectedModCount,使其与集合当前的modCount保持一致 - 同时也会调用集合的删除逻辑,并让
modCount自增 - 相当于“一次修改,两边对齐”,校验自然通过
所以正确写法是:
Iterator<string> it = list.iterator();
while (it.hasNext()) {
String s = it.next();
if ("Python".equals(s)) {
it.remove(); // ✅ 安全
}
}</string>
Fail-Fast 是线程安全机制吗?
不是。它不提供线程安全保证,只负责“发现问题”。
- 单线程下违规修改也会报错(比如 foreach 中调
list.remove()) - 多线程下即使没报错,也不代表安全——
modCount的读写没有同步,存在可见性问题 - 它的定位是开发期辅助工具,帮程序员发现“一边遍历一边改集合”这类逻辑错误
真要并发修改,得换 CopyOnWriteArrayList 或 ConcurrentHashMap 这类 fail-safe 集合。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










