Java集合的快速失败机制核心是modCount字段构成的结构变更感知协议,它严格绑定结构性修改操作,在迭代器中通过快照校验实现轻量一致性检查,单线程下防止并发修改错误。

Java 集合的快速失败(fail-fast)机制核心在于 modCount 字段的设计与协作使用,它不是孤立存在的计数器,而是一套轻量、低开销、高一致性的**结构变更感知协议**。科学设计的关键不在于“怎么加一”,而在于“何时加一”“谁来校验”“如何避免误判”。
modCount 必须严格绑定“结构性修改”语义
结构性修改指改变容器大小或内部链接关系的操作(如 add、remove、clear),不包括 set、get、contains 等只读或元素内容变更操作。例如:
-
ArrayList.add()、remove()、ensureCapacity()→ 触发modCount++ -
ArrayList.set(index, e)、get()、size()→ 不修改modCount -
HashMap.put()触发扩容或链表转红黑树时才增;单纯哈希冲突下的链表追加节点(未触发 rehash)也属于结构性修改,需递增
迭代器必须持有创建时刻的 modCount 快照(expectedModCount)
迭代器在构造时立即读取当前 modCount 值并保存为 expectedModCount。后续每次调用 next() 或 remove() 前,都对比二者是否相等:
- 不等 → 抛
ConcurrentModificationException(非线程安全场景下提示并发修改) - 相等 → 继续执行,并在自身
remove()成功后同步更新expectedModCount(保持与容器一致,允许迭代器安全删除)
这种“快照 + 每次校验”策略避免了锁或 volatile 的开销,也规避了仅靠 final 引用无法检测修改的问题。
立即学习“Java免费学习笔记(深入)”;
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
避免 false positive:禁止在迭代中调用容器自身的修改方法
即使单线程,以下写法也会触发异常,这是设计使然,而非 bug:
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String s = it.next();
if ("b".equals(s)) list.remove(s); // ❌ 触发 CME!
}
正确方式是使用迭代器自己的 remove() 方法(它会同步更新 expectedModCount):
while (it.hasNext()) {
String s = it.next();
if ("b".equals(s)) it.remove(); // ✅ 安全
}
线程安全集合不依赖 modCount 实现 fail-fast
CopyOnWriteArrayList、ConcurrentHashMap 等本身支持并发修改,其迭代器基于快照或弱一致性视图,不检查 modCount,也不会抛 CME。它们的“fail-fast”语义已让位于“fail-safe”。若你在这些集合上看到 modCount 字段,那只是历史兼容或内部实现细节,不参与快速失败逻辑。
本质上,modCount 是一个契约型字段:它不保证线程安全,也不提供原子性,只在单线程下为迭代器提供廉价的结构一致性断言。它的科学性体现在克制——不做多余事,不越界承诺,用最小机制守住最常见错误场景。

















