concurrentmodificationexception 的本质是单线程下的一致性校验:modcount 由集合维护,expectedmodcount 是迭代器创建时的快照;每次 next() 调用 checkforcomodification() 比对二者,不等则抛异常;iterator.remove() 会同步更新 expectedmodcount,而 list.remove() 不会,故导致校验失败。

面试中讲清楚 ConcurrentModificationException 的源码逻辑,关键不是背代码,而是说清“谁在检查、怎么检查、为什么这样设计”。下面从三个层次展开,直击面试官想听的底层逻辑:
modCount 和 expectedModCount 是什么?
这两个变量是理解异常的核心:
-
modCount:定义在
AbstractList中(ArrayList继承它),是集合结构修改的计数器。每次调用add()、remove()、clear()等改变元素个数的操作,都会执行modCount++。 -
expectedModCount:定义在
ArrayList.Itr(内部迭代器类)中,是迭代器创建时记录的 modCount 快照值。构造方法里直接赋值:expectedModCount = modCount。
它们不是线程安全变量,也不涉及 volatile 或 CAS —— 这说明该机制本就不是为多线程同步而生,而是单线程下的一致性校验哨兵。
异常在哪一步触发?看 checkForComodification()
所有迭代动作(如 next()、hasNext())开头都会调用这个私有方法:
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
也就是说,不是删除那一刻抛异常,而是在下一次 next() 检查时才暴露问题。比如遍历到第2个元素后调用了 list.remove(),此时 modCount 已变,但异常要等到第3次调用 next() 才爆发。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
这也解释了为什么增强 for 循环(本质是隐式迭代器)删元素必报错 —— 它完全屏蔽了对 Iterator 对象的控制权。
为什么 Iterator.remove() 不会抛异常?
因为它的实现主动维护了一致性:
- 它先调用
checkForComodification()确保当前状态合法; - 再执行真正的删除逻辑(如
fastRemove()),该方法内部会调用modCount++; -
最后一步:把刚更新的 modCount 赋值给 expectedModCount(即
expectedModCount = modCount)。
所以迭代器自己删自己,相当于“边走边更新地图”,不会触发校验失败。而 list.remove() 只改 modCount,不碰 expectedModCount,等于偷偷撕掉一页地图却没告诉导航员 —— 下次导航必然迷路。
这种设计体现了 fail-fast 的本质:不阻止错误发生,但要在最早可检测点立即中断,避免后续出现更隐蔽的数据错位或数组越界。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










