ConcurrentModificationException 异常的抛出
程序员文章站
2022-05-23 13:33:48
...
ConcurrentModificationException 异常是使用java集合类经常抛出的一种异常。
这种异常常被描述为:快速失败异常,一般是我们程序错误使用导致的,很少会故意允许这种异常发生已保证逻辑上的完整。
那下面讨论什么时候会发生这种异常呢? 以ArrayList为例!
1、单线程中,一边遍历(forEach 和 list.iterator()),一遍删增数据。
深层原因 还是看看源码吧,一切因缘都在源码之中:
2、如果另外一个线程调用了remove 或者 add 或者有类似效果的操作,那么当前iterator迭代 就会抛出ConcurrentModificationException异常。也有可能不抛出,想想为什么?因为modCount 不具备线程可见性,倒是可能会抛出 IndexOutOfBoundsException 异常!!
基于以上分析,在多线程环境中,上面的各自问题就更容易发生! 可能更诡异。
甚至可以说就不需要考虑多线程的情况,因为ArrayList压根就不该在多线程使用!!
这种异常常被描述为:快速失败异常,一般是我们程序错误使用导致的,很少会故意允许这种异常发生已保证逻辑上的完整。
那下面讨论什么时候会发生这种异常呢? 以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压根就不该在多线程使用!!
推荐阅读
-
PHP学堂之聊天室处理异常离线的方法_PHP教程
-
关于python中异常的详细说明
-
Win10系统电脑无法登录LOL提示服务器连接异常的原因及解决方法
-
python try except 捕获所有异常的实例
-
mysql5.7更新操作报异常thisisincompatiblewithsql_mode=only_full_group_by的原因和解决办法
-
,帮忙看看上面代码哪里异常了 JS显示Stack overflow at line:0 复选框全选和单选的时候也有点有关问题
-
ubuntu编译libxml2出现不是nanohttp.c的异常 求指点
-
关于查询异常的相关文章汇总
-
JavaScript中的事件与异常捕获详析
-
简单的实现登录拦截及统一异常处理(自定义异常)