请说明 ConcurrentModificationException 的触发场景,以及它是如何产生的?
考察说明
考查对 Java 集合 Fail-Fast 机制的理解,以及并发或迭代中修改集合的常见误区。
回答思路
- 【回答框架 1】ConcurrentModificationException 是 Java 集合框架中的运行时异常,核心触发条件是迭代器遍历集合时,集合结构被修改(如增加、删除元素),且未通过迭代器自身的方法进行。
- 【回答框架 2】其产生机制基于 modCount 字段。每次结构性修改(add/remove/clear 等)都会使 modCount 自增,迭代器在创建时会记录 expectedModCount,每次 next() 或 hasNext() 检查两者是否一致,不一致则抛出异常。
- 【回答框架 3】常见场景包括:单线程中遍历 ArrayList 时直接调用 list.remove();多线程中一个线程遍历,另一线程修改集合。即使使用 synchronized 或 ConcurrentHashMap,也要注意迭代时的弱一致性语义。
- 【回答框架 4】解决方案有三类:使用迭代器自身的 remove() 方法;使用 CopyOnWriteArrayList 等并发容器,其迭代器基于快照,不抛异常;或者在单线程中先收集需删除的元素,遍历结束后统一删除。
- 【关键点 1】ConcurrentModificationException 由 modCount 与 expectedModCount 不一致触发,属于 Fail-Fast 机制。
- 【关键点 2】迭代器遍历时,任何非迭代器方法的结构性修改都会导致异常。
- 【关键点 3】使用 iterator.remove() 可避免异常,因为它会同步更新 expectedModCount。
- 【关键点 4】CopyOnWriteArrayList 的迭代器基于不可变快照,修改集合时不会抛异常,但迭代期间看不到新增元素。
- 【易错点 1】不要将部分集合(如 ConcurrentHashMap)的弱一致性迭代误认为也不会抛异常,其行为是弱一致但非绝对无异常。
- 【易错点 2】通过 add() 在迭代期间修改集合是常见误用,即使在同一线程也会触发异常。
- 【易错点 3】多线程下即使使用 synchronized 锁保护集合,若在迭代期间不通过迭代器修改,仍可能抛异常,需要额外同步迭代过程。