concurrentmodificationexception 的根本原因是遍历中发生结构性修改,而非迭代器数量;每个迭代器持有独立 expectedmodcount,仅当 modcount 不匹配时触发 fail-fast 机制。

多个迭代器同时操作同一个集合,并不会直接因为“多个迭代器”本身导致异常,真正引发 ConcurrentModificationException 的,是**在任一迭代器遍历过程中,被其他代码(包括另一个迭代器、集合自身方法、甚至同一线程的其他语句)修改了集合结构**。
为什么“多个迭代器”不是问题根源?
每个迭代器在创建时,会把当时集合的 modCount 值复制为自己的 expectedModCount。只要它们各自遍历、互不干扰,且期间没人动集合,就不会出错。比如:
- 线程 A 创建 iterator1 并完成遍历 → 安全
- 线程 B 创建 iterator2 并完成遍历 → 安全
- 但若线程 A 遍历到一半时,线程 B 调用了
list.add()→ iterator1 下次调用next()就抛异常
真正危险的组合:遍历 + 结构修改
异常本质是“快速失败(fail-fast)”机制在起作用——它不关心谁改的,只检查“当前集合实际修改次数(modCount)是否与我当初开始遍历时记录的一致”。常见高危场景包括:
- 用
for-each循环遍历ArrayList,循环体内调用list.remove(x) - 一个线程用
Iterator遍历,另一个线程用list.add()插入元素 - 两个
ListIterator同时操作同一ArrayList,其中一个调用add()或remove() - 主线程遍历中,某个回调或定时任务偷偷修改了该集合
哪些修改会触发异常?
只有**结构性修改(structural modification)** 才会改变 modCount,例如:
-
add()、remove()、clear()、retainAll()、removeAll() -
set()不算结构性修改(不改变大小),不会触发异常 -
get()、contains()等只读操作完全安全
安全修改的正确方式
如果必须在遍历时删/增元素,应严格使用迭代器自身提供的方法:
- 删除:始终用
iterator.remove()(仅限刚next()出来的那个元素) - 添加(仅限
ListIterator):用listIterator.add(),它会同步更新expectedModCount - 批量删除:用
Collection.removeIf(Predicate)(Java 8+),它是原子操作,内部已规避并发检查 - 多线程场景:换用线程安全集合,如
CopyOnWriteArrayList(读多写少)或ConcurrentHashMap










