核心区别在于:当检测到集合结构被修改时,是直接抛出异常停止程序(Fast-fail),还是通过复制副本来保证遍历继续执行而不报错(Fail-safe)。
1. Fast-fail (快速失败)
机制原理
定义:在使用迭代器(Iterator)遍历集合时,如果其他线程(或当前线程在遍历过程中)直接修改了集合的结构(增加、删除元素,但不包括通过迭代器自身的 remove 方法),迭代器会立即检测到并抛出ConcurrentModificationException异常。
实现方式:
- 集合内部维护一个修饰符计数器 modCount。
- 每当集合结构发生变化,modCount 加 1。
- 迭代器在创建时记录当前的 expectedModCount。
- 每次调用 next() 或 hasNext() 时,迭代器检查 modCount 是否等于 expectedModCount。
- 如果不等,说明集合被修改了,立即“快速失败”,抛出异常。
目的:尽早发现错误,防止程序在数据不一致的状态下继续运行,避免产生不可预知的后果(如读取到脏数据或逻辑错乱)。它不是为了解决并发问题,而是为了报错。
典型代表:java.util 包下的集合,如 ArrayList, HashMap, HashSet。
代码示例 (Fast-fail)
importjava.util.*;publicclassFastFailExample{publicstaticvoidmain(String[]args){List<String>list=newArrayList<>();list.add("A");list.add("B");list.add("C");Iterator<String>iterator=list.iterator();while(iterator.hasNext()){Stringitem=iterator.next();if("B".equals(item)){// 【错误操作】直接在遍历过程中修改集合结构// 这会导致 modCount 变化,而 iterator 的 expectedModCount 没变list.remove(item);}}}}运行结果:
抛出 ConcurrentModificationException。程序直接终止
- Fail-safe (安全失败 / 弱一致性)
机制原理
定义:在使用迭代器遍历集合时,即使其他线程修改了原集合,迭代器也不会抛出异常。因为它不是直接遍历原集合,而是遍历原集合的一个副本(快照)。
实现方式:
- 采用 Copy-on-Write (写时复制) 策略。
- 当需要遍历集合时,迭代器会复制一份当前集合内容的快照。
- 后续的遍历操作都在这个快照上进行。
- 原集合的修改(增删改)发生在新的数组上,不影响正在遍历的快照。
目的:保证遍历不中断,提高并发性能。
缺点:
- 内存开销大:每次遍历都要复制一份数据。
- 数据可见性问题(弱一致性):迭代器只能看到创建迭代器那一刻的数据。如果在遍历过程中原集合被修改了,迭代器看不到这些新修改(新增的读不到,删除的依然能读到旧数据)。
典型代表:java.util.concurrent 包下的集合,如 CopyOnWriteArrayList, ConcurrentHashMap (其迭代器是 Fail-safe 的)。
代码示例 (Fail-safe)
importjava.util.*;importjava.util.concurrent.CopyOnWriteArrayList;publicclassFailSafeExample{publicstaticvoidmain(String[]args){// 使用 CopyOnWriteArrayList,它是 Fail-safe 的List<String>list=newCopyOnWriteArrayList<>();list.add("A");list.add("B");list.add("C");Iterator<String>iterator=list.iterator();while(iterator.hasNext()){Stringitem=iterator.next();if("B".equals(item)){// 【安全操作】直接修改原集合,不会抛异常list.remove(item);System.out.println("成功移除了: "+item);}}System.out.println("遍历结束,最终列表: "+list);}}3. 深度对比总结
| 特性 | Fast-fail (快速失败) | Fail-safe (安全失败) |
|---|---|---|
| 发生异常 | 是 (ConcurrentModificationException) | 否 |
| 工作原理 | 直接遍历原集合,检查modCount | 遍历集合的副本/快照 |
| 数据一致性 | 强一致性(一旦修改立即报错,防止脏读) | 弱一致性(可能读到旧数据,看不到新数据) |
| 性能开销 | 低(无额外内存开销) | 高(复制集合需要时间和内存) |
| 适用场景 | 单线程环境,或明确知道不会并发修改的场景 | 高并发读多写少的场景(如白名单、配置列表) |
| 典型类 | ArrayList,HashMap,HashSet | CopyOnWriteArrayList,ConcurrentHashMap |