Java中快速失败 (fail-fast) 机制

快速失败 (fail-fast) 是什么?

fail-fast 是一种错误检测机制,它的核心思想是在检测到错误条件时立即抛出异常,以防止程序进一步执行可能导致错误的操作。通常用于迭代器以及集合类中。当你用迭代器对集合进行迭代时,如果集合在迭代过程中被修改了,就会立即抛出ConcurrentModificationException。这种修改可能是添加、删除集合中的元素。快速失败机制通过立即通知程序员发生了并发修改,避免了未定义行为的出现。

案例 1

public static void main(String[] args) {
        ArrayList<Integer> numbers = new ArrayList<>();
        numbers.add(1);
        numbers.add(2);
        numbers.add(3);
        
        // 创建迭代器
        Iterator<Integer> iterator = numbers.iterator();
        // 在迭代器创建后对列表进行修改
        numbers.add(4);
        // 再次输出迭代器的值
        while (iterator.hasNext()) {
            System.out.println(iterator.next()); // 第一次进来时报错
        }
    }
Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:911)
	at java.util.ArrayList$Itr.next(ArrayList.java:861)
	at org.swp.controller.UserContext.main(UserContext.java:15)

迭代器创建之后,通过调用numbers.add(4)修改了列表的结构,从而在随后的迭代中引发了ConcurrentModificationException

案例 2

public static void main(String[] args) {
	ArrayList<Integer> numbers = new ArrayList<>();
	numbers.add(1);
	numbers.add(2);
	numbers.add(3);
		// 其实在代码编译的时候for循环会被编译成迭代器
	for(Integer a : numbers){ // 第二次遍历时报错
		if(a.equals(1)){
			numbers.remove(a);
		}
	}
}
Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:911)
	at java.util.ArrayList$Itr.next(ArrayList.java:861)
	at org.swp.controller.UserContext.main(UserContext.java:15)

当迭代器正在遍历numbers列表时,你尝试从中删除元素。这会改变列表的结构,因此触发了ConcurrentModificationException

如何避免这个异常

要修复这种错误,你需要确保在迭代期间不直接修改集合。如果你必须修改集合,你有几个选项:

  • 在迭代期间收集要删除的元素,然后在迭代后删除它们。
  • 使用迭代器提供的remove()方法来移除元素。
  • 使用支持并发修改的集合类,如ConcurrentHashMap或CopyOnWriteArrayList。
  • 在for循环中引入变量,而不是遍历集合
ArrayList<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);

        for(int i=0;i<list.size();i++) {
            Integer item = list.get(i);
            if (item.equals(3)) {
                list.remove(i);//为了效率,这里最好不要用list.remove(item)
            }
        }

不引用迭代器方法进行删除,可以防止ConcurrentModificationException

Iterator<Integer> iterator = numbers.iterator();
while (iterator.hasNext()) {
    Integer a = iterator.next();
    if (a.equals(1)) {
        iterator.remove();
    }
}

使用迭代器的remove()方法来删除当前元素可以防止ConcurrentModificationException,因为这样做不会破坏迭代器的内部状态。

public static void main(String[] args) {
        CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);

        Iterator<Integer> iterator = list.iterator();
        while (iterator.hasNext()) {
            Integer a = iterator.next();
            if (a.equals(1)) {
                list.remove(a);
            }
        }
        System.out.println(list);
    }

CopyOnWriteArrayList 源码推荐阅读文章
文章链接: 解析CopyOnWriteArrayList的高效读取与安全更新

你可能感兴趣的:(Java,java,集合,迭代器,interator,快速失败fail-fast)