ConcurrentModificationException 异常的抛出
程序员文章站
2022-04-18 17:21:46
...
ConcurrentModificationException 异常是使用java集合类经常抛出的一种异常。
这种异常常被描述为:快速失败异常,一般是我们程序错误使用导致的,很少会故意允许这种异常发生已保证逻辑上的完整。
那下面讨论什么时候会发生这种异常呢? 以ArrayList为例!
1、单线程中,一边遍历(forEach 和 list.iterator()),一遍删增数据。
深层原因 还是看看源码吧,一切因缘都在源码之中:
2、如果另外一个线程调用了remove 或者 add 或者有类似效果的操作,那么当前iterator迭代 就会抛出ConcurrentModificationException异常。也有可能不抛出,想想为什么?因为modCount 不具备线程可见性,倒是可能会抛出 IndexOutOfBoundsException 异常!!
[b]基于以上分析,在多线程环境中,上面的各自问题就更容易发生! 可能更诡异。
甚至可以说就不需要考虑多线程的情况,因为ArrayList压根就不该在多线程使用!![/b]
这种异常常被描述为:快速失败异常,一般是我们程序错误使用导致的,很少会故意允许这种异常发生已保证逻辑上的完整。
那下面讨论什么时候会发生这种异常呢? 以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 异常!!
[b]基于以上分析,在多线程环境中,上面的各自问题就更容易发生! 可能更诡异。
甚至可以说就不需要考虑多线程的情况,因为ArrayList压根就不该在多线程使用!![/b]
上一篇: MVP初级练习 注册登陆展示
推荐阅读
-
安装Visual Studio后所有程序出异常它霸道的管着 启用/禁用Visual studio实时调试
-
Oracle ORA-22908(NULL表值的参考)异常分析与解决方法
-
ABP vNext 不使用工作单元为什么会抛出异常
-
详解JavaScript中的异常处理方法
-
java 集合并发操作出现的异常ConcurrentModificationException
-
从0开始的Python学习016异常
-
A5营销:网站“抓取异常”问题的解决方案
-
太狗血了!分享一次网站百度收录排名异常的检查记录
-
VS2015在升级到Update2之后运行Cordova项目异常的解决方案
-
Java 异常的知识整理