Iterator Fail-Fast
Fail-Fast(快速失败)是 Java 集合框架中的一种错误检测机制。当多个线程(或单线程)在遍历集合的同时修改集合结构时,迭代器会快速抛出 ConcurrentModificationException,而不是在将来某个不确定的时间出现不确定的行为。
一、什么是 Fail-Fast
1.1 定义
Fail-Fast:在迭代过程中,如果检测到集合结构被修改(非迭代器自身的修改),立即抛出 ConcurrentModificationException。
1.2 结构修改
| 操作 | 是否结构修改 |
|---|---|
add(e) | ✅ 是 |
remove(e) | ✅ 是 |
clear() | ✅ 是 |
set(index, e) | ❌ 否(仅修改值) |
iterator.remove() | ❌ 否(迭代器自身操作) |
iterator.add(e) | ❌ 否(ListIterator 自身操作) |
二、Fail-Fast 原理
2.1 modCount 机制
集合类内部维护一个 modCount 变量,记录集合的修改次数。
public class ArrayList<E> {
// 修改次数
protected transient int modCount = 0;
public boolean add(E e) {
modCount++; // 每次修改 +1
// ...
}
public E remove(int index) {
modCount++; // 每次修改 +1
// ...
}
}2.2 迭代器检测
迭代器在创建时记录 expectedModCount,每次迭代时检查是否一致。
private class Itr implements Iterator<E> {
// 创建迭代器时的 modCount
int expectedModCount = modCount;
public E next() {
// 检查是否有并发修改
checkForComodification();
// ...
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}2.3 流程图
┌─────────────────────────────────────────────────────────┐
│ Fail-Fast 检测流程 │
├─────────────────────────────────────────────────────────┤
│ │
│ 1. 创建迭代器 │
│ expectedModCount = modCount (例如:5) │
│ │
│ 2. 遍历过程中 │
│ 每次调用 next() │
│ │ │
│ ▼ │
│ checkForComodification() │
│ │ │
│ ▼ │
│ modCount == expectedModCount ? │
│ ┌───┴───┐ │
│ 是 否 │
│ │ │ │
│ ▼ ▼ │
│ 继续遍历 抛出 ConcurrentModificationException │
│ │
└─────────────────────────────────────────────────────────┘三、示例代码
3.1 触发 Fail-Fast
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
// 单线程遍历时修改集合
for (String s : list) {
System.out.println(s);
if (s.equals("B")) {
list.remove(s); // 抛出 ConcurrentModificationException
}
}
// 原因:foreach 循环使用迭代器,list.remove() 修改了 modCount
// 但迭代器的 expectedModCount 没有更新,导致不一致3.2 使用迭代器删除
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
// 使用迭代器删除
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String s = iterator.next();
if (s.equals("B")) {
iterator.remove(); // ✅ 正确,迭代器会同步 expectedModCount
}
}
System.out.println(list); // [A, C]3.3 迭代器 remove 源码
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount; // 同步 modCount
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}四、Fail-Fast vs Fail-Safe
4.1 对比
| 对比项 | Fail-Fast | Fail-Safe |
|---|---|---|
| 机制 | 直接操作原集合 | 复制原集合 |
| 修改检测 | 检测到修改抛异常 | 不检测,正常遍历 |
| 内存开销 | 低 | 高(需要复制) |
| 实时性 | 高(反映最新状态) | 低(遍历副本) |
| 典型实现 | ArrayList, HashMap | CopyOnWriteArrayList, ConcurrentHashMap |
4.2 Fail-Safe 示例
// CopyOnWriteArrayList - Fail-Safe
List<String> list = new CopyOnWriteArrayList<>();
list.add("A");
list.add("B");
list.add("C");
for (String s : list) {
System.out.println(s);
if (s.equals("B")) {
list.remove(s); // ✅ 不会抛异常
}
}
// 输出:A, B, C(遍历的是副本,remove 影响原集合但不影响遍历)
System.out.println(list); // [A, C]
// ConcurrentHashMap 的迭代器
Map<String, Integer> map = new ConcurrentHashMap<>();
map.put("A", 1);
map.put("B", 2);
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey());
map.remove("B"); // ✅ 不会抛异常
}4.3 CopyOnWriteArrayList 原理
public class CopyOnWriteArrayList<E> {
private transient volatile Object[] array;
public boolean add(E e) {
synchronized (lock) {
Object[] elements = getArray();
int len = elements.length;
// 复制原数组,添加新元素
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
retu