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压根就不该在多线程使用!!
分享到:
相关推荐
因此,ConcurrentModificationException 异常是为了确保集合的遍历正确性而抛出的异常。它提示我们,在遍历集合时,不要同时修改集合,以免出现不正确的遍历结果。 在实际开发中,我们可以使用 ListIterator 或者...
当迭代器检测到集合在迭代过程中被修改,它会立即抛出`ConcurrentModificationException`,而不是尝试继续迭代或返回不确定的结果。 在描述中提到了Map的遍历方式,通常我们会通过获取Map的键集(keySet)或值集...
Fail-fast机制是一种快速失败机制,在集合遍历过程中,一旦发现容器中的数据被修改了,会立刻抛出ConcurrentModificationException异常,从而导致遍历失败。这种机制下的集合容器,例如HashMap和ArrayList等,都是...
当你在一个迭代器正在遍历集合的过程中添加或删除元素时,就会抛出这个异常。这个问题在单线程环境下不会出现,但在多线程并发场景下,如果多个线程同时修改一个集合,就可能导致`ConcurrentModificationException`...
5. **NumberFormatException**:在将字符串转换为数值类型(如`Integer.parseInt()`或`Double.parseDouble()`)时,如果字符串不能被解析为有效数值,会抛出此异常。确保转换前字符串符合数值格式。 6. **...
在集合操作中,常见的异常有`NullPointerException`(当访问或操作空对象时抛出)、`IndexOutOfBoundsException`(当索引超出集合边界时抛出)、`ConcurrentModificationException`(多线程环境下并发修改集合时抛出...
这段代码可能会抛出ConcurrentModificationException异常,因为在遍历过程中,集合的内容被修改了。 要避免这种情况,可以使用迭代器的remove方法来删除当前迭代对象,而不是使用集合的remove方法: Iterator...
7. **throw和throws**:`throw`用于在代码中抛出一个异常实例,而`throws`用于方法签名,声明该方法可能抛出的异常。 8. **异常处理最佳实践**: - 不要忽视异常,即使捕获了异常也要进行适当的处理。 - 避免在`...
当集合在迭代期间被修改,`Iterator`无法正确地更新其内部状态,从而抛出异常,提示开发者这种行为是不被允许的。 在标题所提及的场景中,问题可能出在以下代码示例: ```java List<String> list = new ArrayList...
当try块中的代码抛出异常时,catch块可以捕获并处理它,finally块则确保无论是否发生异常,某些代码总会被执行,比如资源关闭。 - **异常类型匹配**:catch块应根据预期的异常类型进行匹配,避免使用过于宽泛的...
10. **ConcurrentModificationException**:在多线程环境下,当一个线程正在修改集合,而另一个线程尝试迭代该集合时,会抛出此异常。应使用并发友好的集合类或同步控制来避免这个问题。 11. **ServletException: ...
如果在遍历过程中,集合结构发生了变化(如添加、删除或修改元素),迭代器无法预测这些变化,因此会抛出`ConcurrentModificationException`。在上述示例代码中,我们创建了一个HashMap并使用迭代器遍历其键集。在...
`ConcurrentModificationException`是Java中常见的并发异常,通常在多线程环境下,当一个线程正在修改集合(例如添加或删除元素),而另一个线程尝试迭代同一集合时,就会抛出此异常。 Axis1是一个开放源码的Web...
这是因为ArrayList的迭代器不支持并发修改,一旦检测到集合状态在迭代过程中被修改,就会抛出异常。为了在遍历过程中修改ArrayList,可以使用`Iterator`的`remove()`方法,或者使用`CopyOnWriteArrayList`这样的线程...
这种并发修改违反了集合类的内部状态,因此抛出该异常。下面我们将深入探讨这个问题的原因、示例代码以及解决策略。 **原因分析:** 在 Java 中,迭代器(Iterator)是用于遍历集合的专用工具,它提供了 `hasNext()...
故障安全迭代器在迭代过程中不会抛出ConcurrentModificationException异常,它们使用的是集合的一个快照,因此在多线程环境下使用比较安全,不会抛出异常。不过,这种迭代器的缺点是无法保证遍历到最新的数据,因为...
只读迭代:由于写时复制的机制,CopyOnWriteArraySet 的迭代器是只读的,即迭代过程中不会抛出 ConcurrentModificationException 异常。但是,迭代器获得的数据可能不包含最新的修改。 较高的内存占用:由于每次...
**描述**:当试图访问一个空对象的属性或方法时会抛出此异常。这是Java中最常见的运行时异常之一。 **示例代码**: ```java String str = null; System.out.println(str.length()); ``` **解决方案**: 1. **检查...
- `ConcurrentModificationException`:当多线程并发修改集合时,可能抛出此异常。 - `NullPointerException`:当尝试访问或操作null对象引用时抛出。 - `IllegalArgumentException`:当传递给方法的参数无效时抛出...