`

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压根就不该在多线程使用!!
1
5
分享到:
评论

相关推荐

    java.util.ConcurrentModificationException 异常问题详解1

    因此,ConcurrentModificationException 异常是为了确保集合的遍历正确性而抛出的异常。它提示我们,在遍历集合时,不要同时修改集合,以免出现不正确的遍历结果。 在实际开发中,我们可以使用 ListIterator 或者...

    java 集合并发操作出现的异常ConcurrentModificationException

    当迭代器检测到集合在迭代过程中被修改,它会立即抛出`ConcurrentModificationException`,而不是尝试继续迭代或返回不确定的结果。 在描述中提到了Map的遍历方式,通常我们会通过获取Map的键集(keySet)或值集...

    【面试普通人VS高手系列】Fail-safe机制与Fail-fast机制分别有什么作用.doc

    Fail-fast机制是一种快速失败机制,在集合遍历过程中,一旦发现容器中的数据被修改了,会立刻抛出ConcurrentModificationException异常,从而导致遍历失败。这种机制下的集合容器,例如HashMap和ArrayList等,都是...

    遍历并批量删除容器中元素出现ConcurrentModificationException原因及处置

    当你在一个迭代器正在遍历集合的过程中添加或删除元素时,就会抛出这个异常。这个问题在单线程环境下不会出现,但在多线程并发场景下,如果多个线程同时修改一个集合,就可能导致`ConcurrentModificationException`...

    java常见的异常

    5. **NumberFormatException**:在将字符串转换为数值类型(如`Integer.parseInt()`或`Double.parseDouble()`)时,如果字符串不能被解析为有效数值,会抛出此异常。确保转换前字符串符合数值格式。 6. **...

    集合自定义异常

    在集合操作中,常见的异常有`NullPointerException`(当访问或操作空对象时抛出)、`IndexOutOfBoundsException`(当索引超出集合边界时抛出)、`ConcurrentModificationException`(多线程环境下并发修改集合时抛出...

    Java中增强for循环的实现原理和坑详解

    这段代码可能会抛出ConcurrentModificationException异常,因为在遍历过程中,集合的内容被修改了。 要避免这种情况,可以使用迭代器的remove方法来删除当前迭代对象,而不是使用集合的remove方法: Iterator...

    总结java的30个异常及方案

    7. **throw和throws**:`throw`用于在代码中抛出一个异常实例,而`throws`用于方法签名,声明该方法可能抛出的异常。 8. **异常处理最佳实践**: - 不要忽视异常,即使捕获了异常也要进行适当的处理。 - 避免在`...

    Iterator遍历过程中list删除导致异常

    当集合在迭代期间被修改,`Iterator`无法正确地更新其内部状态,从而抛出异常,提示开发者这种行为是不被允许的。 在标题所提及的场景中,问题可能出在以下代码示例: ```java List&lt;String&gt; list = new ArrayList...

    Java Web应用中常见的错误和异常问题处理.zip

    当try块中的代码抛出异常时,catch块可以捕获并处理它,finally块则确保无论是否发生异常,某些代码总会被执行,比如资源关闭。 - **异常类型匹配**:catch块应根据预期的异常类型进行匹配,避免使用过于宽泛的...

    Java常见异常集-Java,Hibernate,Tomcat异常

    10. **ConcurrentModificationException**:在多线程环境下,当一个线程正在修改集合,而另一个线程尝试迭代该集合时,会抛出此异常。应使用并发友好的集合类或同步控制来避免这个问题。 11. **ServletException: ...

    出现java.util.ConcurrentModificationException 问题及解决办法

    如果在遍历过程中,集合结构发生了变化(如添加、删除或修改元素),迭代器无法预测这些变化,因此会抛出`ConcurrentModificationException`。在上述示例代码中,我们创建了一个HashMap并使用迭代器遍历其键集。在...

    axis1.4.1.zip

    `ConcurrentModificationException`是Java中常见的并发异常,通常在多线程环境下,当一个线程正在修改集合(例如添加或删除元素),而另一个线程尝试迭代同一集合时,就会抛出此异常。 Axis1是一个开放源码的Web...

    Java源码解析ArrayList及ConcurrentModificationException

    这是因为ArrayList的迭代器不支持并发修改,一旦检测到集合状态在迭代过程中被修改,就会抛出异常。为了在遍历过程中修改ArrayList,可以使用`Iterator`的`remove()`方法,或者使用`CopyOnWriteArrayList`这样的线程...

    java.util.ConcurrentModificationException 解决方法

    这种并发修改违反了集合类的内部状态,因此抛出该异常。下面我们将深入探讨这个问题的原因、示例代码以及解决策略。 **原因分析:** 在 Java 中,迭代器(Iterator)是用于遍历集合的专用工具,它提供了 `hasNext()...

    java-大数据基础面试思考.pdf

    故障安全迭代器在迭代过程中不会抛出ConcurrentModificationException异常,它们使用的是集合的一个快照,因此在多线程环境下使用比较安全,不会抛出异常。不过,这种迭代器的缺点是无法保证遍历到最新的数据,因为...

    java集合-CopyOnWriteArraySet的使用

    只读迭代:由于写时复制的机制,CopyOnWriteArraySet 的迭代器是只读的,即迭代过程中不会抛出 ConcurrentModificationException 异常。但是,迭代器获得的数据可能不包含最新的修改。 较高的内存占用:由于每次...

    java常见错误集合以及描述

    **描述**:当试图访问一个空对象的属性或方法时会抛出此异常。这是Java中最常见的运行时异常之一。 **示例代码**: ```java String str = null; System.out.println(str.length()); ``` **解决方案**: 1. **检查...

    corejava面试题

    - `ConcurrentModificationException`:当多线程并发修改集合时,可能抛出此异常。 - `NullPointerException`:当尝试访问或操作null对象引用时抛出。 - `IllegalArgumentException`:当传递给方法的参数无效时抛出...

Global site tag (gtag.js) - Google Analytics