Java集合框架的fail-fast机制通过modCount与expectedModCount校验实现,核心是检测即报错而非同步;modCount记录结构性修改次数,迭代器初始化时复制该值为expectedModCount,后续每次next()前校验二者是否相等,不等则抛ConcurrentModificationException。

Java 集合框架的迭代器通过 modCount 与 expectedModCount 的版本校验 实现快速失败(fail-fast),核心不是加锁或同步,而是“检测即报错”。
modCount 和 expectedModCount 的协同校验
每个支持 fail-fast 的集合(如 ArrayList、HashMap、HashSet)内部都维护一个 modCount 字段,它是整型计数器,记录集合结构性修改(add、remove、clear 等)的次数。每次结构变更,modCount 自动递增。
当调用 collection.iterator() 创建迭代器时,迭代器会把当前 modCount 的值复制到自己的 expectedModCount 属性中。
此后每次调用 next() 或 hasNext() 前,迭代器都会执行校验:
立即学习“Java免费学习笔记(深入)”;
- 若
modCount == expectedModCount:继续迭代 - 若
modCount != expectedModCount:立即抛出ConcurrentModificationException
为什么直接调用集合方法会触发异常
因为集合自身的 add()、remove()、clear() 等方法会更新 modCount,但不会通知已存在的迭代器更新其 expectedModCount。
例如:
-
iterator.next()→ 检查通过 -
list.remove("x")→ modCount 加 1,expectedModCount 不变 - 再次
iterator.next()→ 校验失败,抛异常
这说明:异常不是由“多线程”触发的必要条件,单线程中违反“仅用迭代器修改”的约定同样会失败。
为什么 iterator.remove() 是安全的
迭代器自身的 remove() 方法是特例:它在删除元素的同时,会同步更新自身持有的 expectedModCount,使其与集合当前的 modCount 保持一致。
因此,合法使用迭代器删除不会破坏一致性,也不会触发异常。
这个设计让开发者能明确区分“遍历行为”和“修改行为”,把修改权收归迭代器统一管理。
fail-fast 的定位是检测 Bug,不是线程安全机制
它不提供原子性、也不保证可见性,不能替代同步或并发集合。
关键事实:
- 无法检测非结构性修改(如
list.set(i, x)不改 modCount,不触发异常) - 不保证 100% 检测到并发修改(因无同步,存在指令重排或缓存可见性问题)
- 异常只用于暴露逻辑错误,不可作为程序控制流的判断依据
真正需要并发修改的场景,应选用 CopyOnWriteArrayList(fail-safe)、ConcurrentHashMap 等线程安全替代方案。


















