详解fail-fast产生的原因

运行以下程序:

import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;

public class Fail {
	public static void main(String[] args) {
		List <Integer>list=new ArrayList<>();
		list.add(9);
		list.add(5);
		list.add(2);
		list.add(7);
		Iterator <Integer>iter=list.iterator();
		while(iter.hasNext()) {
			list.add(5);
			System.out.println(iter.next());
		}
	}
}

我们希望看到的时遍历打印list中的数值,并在遍历的过程中,往list中添加数据。

但实际的运行结果却是:

抛出了一个 java.util.ConcurrentModificationException ,这就是我们所说的fail-fast,即快速失败。

那他产生的原因是什么呢?

首先我们找到:java.util.ArrayList$Itr.next   ---- 这个是ArrayList 的内部类Itr中的一个方法,它实现了Iterator<E>接口。我们遍历list集合是就是调用的这个方法。

public E next() {
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }

可以看到,在第二行有一个: checkForComodification(); 异常就是从这里抛出来的,打开这个方法。

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }

可以看到 ,该方法比较了modCount  , expectedModCount 这两个值,当他们不相等时就会抛出 ConcurrentModifcationException .

通过源码找到 expectedModCount :

他在Itr这个类中定义,当这个类被实例化时,他被初始化成modCount

我们再来找到modCount:

他被定义在AbstractList中,且被初始化为0。前面的英文是对他作用的说明,通过第一段我们就可以知道他的作用:

The number of times this list has been structurally modified.Structural modifications are those that change the size of the list, or otherwise perturb it in such a fashion that iterations in progress may yield incorrect results.

这段话的意思是:这个数记录了这个列表在结构上被修改的次数。结构修改是指改变列表的大小,或者以某种方式干扰列表,使正在进行的迭代可能产生不正确的结果。

在ArrayList中具体哪些操作会改变modCount的值?通过源码可以看到:add()系列,remove()系列,clear(),replace()等,都会使得modCout++ 。(源码就不截出来,大家可以通过JDK源码自己去看)

需要注意的是: list中的set(index,element)等操作并不会改变modCout的值,因此我们一定要深刻理解什么是结构修改。

 

那么通过以上的探究这个异常产生的原因就非常的清楚了:我们在遍历一个Collection集合时,会调用该类的  .Iterator();则他会建一个Itr的对象,此时Itr类中:expectedModCount就会被初始化为当前的modCount。如果此时我们对Collection集合类进行结构修改时,modCount的值就会改变,而在调用next()时,会先比较这两个值是否相等,此时他们不相等,就抛出了ConcurrentModificationException.

在Itr中,还有remove(),forEachRemaining(Consumer<? super E> action),也会进行checkForComodification(),因此在使用Iterator的时候,都不可以对该集合类进行结构化修改。

java中的语法糖foreach实际是对Iterator进行包装,因此遍历的过程中对集合进行结构化的修改也会抛出这个异常。

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值