ConcurrentModificationException 异常的抛出

ConcurrentModificationException 异常是使用java集合类经常抛出的一种异常。

这种异常常被描述为:快速失败异常,一般是我们程序错误使用导致的,很少会故意允许这种异常发生已保证逻辑上的完整。

那下面讨论什么时候会发生这种异常呢? 以ArrayList为例!

1、单线程中,一边遍历(forEach 和 list.iterator()),一遍删增数据。


       List<Integer> list = new ArrayList<Integer>();
       for(int i=0;i<10;i++){
           list.add(i);
       }
       for(int i: list){  //等同 list.iterator()的效果
           list.add(i);
       }
    


深层原因 还是看看源码吧,一切因缘都在源码之中:
private class Itr implements Iterator<E> {
	/**
	 * Index of element to be returned by subsequent call to next.
	 游标 开始从0获取数据
	 */
	int cursor = 0;

	/**
	   最近一次返回的数据的index,如果调用了remove ,那么返回-1
	 */
	int lastRet = -1;

	/**
	   modCount 标识list结构改变的次数, modCount 是没有volitile 修饰的!!,所以在多线程环境中 这个逻辑有可能不会快速失败,
	   这也从另外一个方面证明:ArrayList压根就不能在线程不安全的环境中使用!!! 使用就是个错误!!
	   
	   另外:expectedModCount 只有通过Iterator的 remove 方法操作 才会修改这个值,保证不会抛出ConcurrentModificationException ,
	   如果调用ArrayList的add 或者remove等修改底层结构的操作,那么expectedModCount != modCount 也就会发生ConcurrentModificationException 异常喽
	 */
	int expectedModCount = modCount;

	public boolean hasNext() {
            return cursor != size();
	}

	public E next() {
            checkForComodification();
	    try {
		E next = get(cursor);
		lastRet = cursor++;//当前游标也要动动哦
		return next;
	    } catch (IndexOutOfBoundsException e) { //为什么会抛出异常? 因为:checkForComodification()之后,可能立即有其他线程变动了底层的数组结构的(多线程环境下不安全的!!)
		checkForComodification(); //一旦抛出此异常,可以认定会抛出checkForComodification异常喽!
		throw new NoSuchElementException();
	    }
	}

	public void remove() {
	    if (lastRet == -1)
		throw new IllegalStateException(); //如果连续remove两次,会有问题的,这里就是证据!!
            checkForComodification();
	    try {
		AbstractList.this.remove(lastRet);
		if (lastRet < cursor)
		    cursor--; //cursor 为什么会 --?? 因为:ArrayList列表lastRet删除后,发送了数组的迁移复制。
		  lastRet = -1;
		   expectedModCount = modCount; //必须的,也是只有这里才能设置 expectedModCount
	    } catch (IndexOutOfBoundsException e) { 
		throw new ConcurrentModificationException();
	    }
	}

//检测expectedModCount 和 modCount 是否一致,如果不一致可以认定,在迭代的过程中list发生了结构的改变
	final void checkForComodification() {
	    if (modCount != expectedModCount)
		throw new ConcurrentModificationException();
	}
    }


2、如果另外一个线程调用了remove 或者 add 或者有类似效果的操作,那么当前iterator迭代 就会抛出ConcurrentModificationException异常。也有可能不抛出,想想为什么?因为modCount 不具备线程可见性,倒是可能会抛出 IndexOutOfBoundsException 异常!!



基于以上分析,在多线程环境中,上面的各自问题就更容易发生! 可能更诡异。
甚至可以说就不需要考虑多线程的情况,因为ArrayList压根就不该在多线程使用!!

你可能感兴趣的:(java,多线程)