modcount是集合类中记录结构性修改次数的int字段,用于fail-fast机制;迭代器通过比对modcount与expectedmodcount校验并发修改,单线程遍历时直接调用集合remove()也会触发异常,其目的非线程安全而是快速暴露bug。

Java 集合类(如 ArrayList、HashMap)中的 fail-fast 机制,本质是通过 modCount(修改计数器)与迭代器内部保存的 expectedModCount 做校验来实现的——一旦发现两者不一致,就立即抛出 ConcurrentModificationException。
modCount 是什么
modCount 是集合类(如 AbstractList、AbstractMap)中定义的一个 int 类型字段,用于记录集合**结构性修改**的次数。所谓结构性修改,是指会改变集合大小或内部结构的操作,比如:
• add() / remove()
• clear()
• addAll() / removeAll()
而仅修改元素值(如 set() 修改某个索引处的元素)通常不会增加 modCount。
迭代器如何利用 modCount 实现校验
当调用集合的 iterator() 方法时,其返回的迭代器(如 ArrayList.Itr)会在构造时将当前集合的 modCount 值复制到自己的 expectedModCount 字段中。
此后每次调用 next() 或 remove() 前,迭代器都会检查:
• 如果 modCount != expectedModCount → 立即抛出 ConcurrentModificationException。
这个检查发生在方法入口(例如 checkForComodification() 被频繁调用),所以“失败”非常及时,而非等到遍历结束才察觉。
为什么单线程下也会触发 fail-fast
常见误区是认为 fail-fast 只防多线程并发修改。其实只要在迭代过程中,**由同一个线程直接调用集合自身的修改方法**,就会触发:
• 错误写法:在 for-each 或 Iterator 遍历时,用 list.remove() 删除元素
• 正确做法:必须使用迭代器自身的 it.remove()(它会同步更新 expectedModCount)
因为 it.remove() 内部会先调用集合的 remove()(使 modCount++),再把新值赋给 expectedModCount,从而保持一致。
fail-fast 不是线程安全保证
modCount 没有被声明为 volatile,也不涉及同步块。它的设计目标不是解决竞态条件,而是快速暴露 bug——提醒开发者:你在遍历时意外修改了集合结构。
若需真正线程安全的遍历,应选用:
• 并发容器(如 CopyOnWriteArrayList、ConcurrentHashMap)
• 或加锁 + 显式同步控制
这些方案不依赖 modCount 校验,而是从设计上规避冲突。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











