java.util.ConcurrentModificationException异常原因及解决方法
在java语言中,ArrayList是一个很常用的类,在编程中经常要对ArrayList进行删除操作,在使用remove方法对ArrayList进行删除操作时,报java.util.ConcurrentModificationException异常,下面探讨一下该异常的原因以及解决办法。
1 import java.util.ArrayList;
2 import java.util.List;
3
4 public class Test {
5
6 public static void main(String[] args) {
7 // TODO Auto-generated method stub
8 List<Integer> listA=new ArrayList<>();
9 listA.add(1);
10 listA.add(2);
11 listA.add(3);
12 listA.add(4);
13 listA.add(5);
14 listA.add(6);
15
16 for(Integer a:listA){
17 if (a==3) {
18 listA.remove(3);
19 }
20 }
21 }
22 }
上述代码在删除value=3的元素时,报java.util.ConcurrentModificationException异常,如下图
1 Exception in thread "main" java.util.ConcurrentModificationException
2 at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
3 at java.util.ArrayList$Itr.next(ArrayList.java:851)
4 at com.zhang.Test.main(Test.java:19)
那么是不是删除每一个元素都会报上面的异常呢?经过测试发现,删除value=5的元素(倒数第二个元素)时,就不会报上面异常,除此之外均会报如上异常。(此处不再一一测试,有兴趣可自己编码测试)
我们通过上述测试发现了规律,既除了删除倒数第二个错误不会异常,删除其他元素均会异常。既然掌握了规律,那么就要从源码层面揭露该异常的原因。首先发现Java的for循环,就是将List对象遍历托管给Iterator,你如果要对list进行增删操作,都必须经过Iterator,否则Iterator遍历时会乱,所以直接对list进行删除时,Iterator会抛出ConcurrentModificationException异常。
其实,每次foreach迭代的时候都有两部操作:
第一步:iterator.hasNext() //判断是否有下个元素
public boolean hasNext() {
return cursor != size;
}
第二步:item = iterator.next() //下个元素是什么,并赋值给上面例子中的item变量
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
通过debug调试,我们发现,checkForComodification时返回了异常,异常原因为 modCount != expectedModCount。
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
进一步阅读源码,发现:
1.modCount 时List从new开始,被修改的次数。当List调用Remove等方法时,modCount++
2.expectedModCount是指Iterator现在期望这个list被修改的次数是多少次。是在Iterator初始化的时候将modCount 的值赋给了expectedModCount
那么就解释了为什么会报上述异常:
1.modCount 会随着调用List.remove方法而自动增减,而expectedModCount则不会变化,就导致modCount != expectedModCount。
2.在删除倒数第二个元素后,cursor=size-1,此时size=size-1,导致hasNext方法认为遍历结束。
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
List<Integer> listA=new ArrayList<>();
listA.add(1);
listA.add(2);
listA.add(3);
listA.add(4);
listA.add(5);
listA.add(6);
for(Integer a:listA){
System.out.println(a);
if (a==5) {
listA.remove(5);
}
}
}
}
上述代码加了打印后,输出1,2,3,4,5;进一步证明最后一个元素6并没有被遍历到。
解决方法1:
从API中可以看到List等Collection的实现并没有同步化,如果在多 线程应用程序中出现同时访问,而且出现修改操作的时候都要求外部操作同步化;调用Iterator操作获得的Iterator对象在多线程修改Set的时 候也自动失效,并抛出java.util.ConcurrentModificationException。这种实现机制是fail-fast,对外部 的修改并不能提供任何保证。
网上查找的关于Iterator的工作机制。Iterator是工作在一个独立的线程中,并且拥有一个 mutex锁,就是说Iterator在工作的时候,是不允许被迭代的对象被改变的。Iterator被创建的时候,建立了一个内存索引表(单链表),这 个索引表指向原来的对象,当原来的对象数量改变的时候,这个索引表的内容没有同步改变,所以当索引指针往下移动的时候,便找不到要迭代的对象,于是产生错 误。List、Set等是动态的,可变对象数量的数据结构,但是Iterator则是单向不可变,只能顺序读取,不能逆序操作的数据结构,当 Iterator指向的原始数据发生变化时,Iterator自己就迷失了方向。
如何才能满足需求呢,需要再定义一个List,用来保存需要删除的对象:
List delList = new ArrayList();
最后只需要调用集合的removeAll(Collection con)方法就可以了。
解决方法2:
在找到原因后,则进一步进行解决
经过查阅源码可以发现,iterator也有一个remove方法如下,其中有一个重要的操作为expectedModCount = modCount;这样就保证了两者的相等。
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
修改后的代码如下:
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
List<Integer> listA=new ArrayList<>();
listA.add(1);
listA.add(2);
listA.add(3);
listA.add(4);
listA.add(5);
listA.add(6);
Iterator<Integer> it_b=listA.iterator();
while(it_b.hasNext()){
Integer a=it_b.next();
if (a==4) {
it_b.remove();
}
}
for(Integer b:listA){
System.out.println(b);
}
}
}
java.util.ConcurrentModificationException异常完美解决。
java.util.ConcurrentModificationException异常原因及解决方法的更多相关文章
- java.util.ConcurrentModificationException 异常原因和解决方法
不要在 foreach 循环里进行元素的 remove/add 操作.remove 元素请使用 Iterator方式,如果并发操作,需要对 Iterator 对象加锁. 注意: 1.foreach遍历 ...
- Java ConcurrentModificationException异常原因和解决方法
Java ConcurrentModificationException异常原因和解决方法 在前面一篇文章中提到,对Vector.ArrayList在迭代的时候如果同时对其进行修改就会抛出java.u ...
- Java并发编程:Java ConcurrentModificationException异常原因和解决方法
Java ConcurrentModificationException异常原因和解决方法 在前面一篇文章中提到,对Vector.ArrayList在迭代的时候如果同时对其进行修改就会抛出java.u ...
- 【转】Java ConcurrentModificationException异常原因和解决方法
原文网址:http://www.cnblogs.com/dolphin0520/p/3933551.html Java ConcurrentModificationException异常原因和解决方法 ...
- 9、Java ConcurrentModificationException异常原因和解决方法
Java ConcurrentModificationException异常原因和解决方法 在前面一篇文章中提到,对Vector.ArrayList在迭代的时候如果同时对其进行修改就会抛出java.u ...
- (转)Java ConcurrentModificationException异常原因和解决方法
转载自:http://www.cnblogs.com/dolphin0520/p/3933551.html 在前面一篇文章中提到,对Vector.ArrayList在迭代的时候如果同时对其进行修改就会 ...
- Java ConcurrentModificationException异常原因和解决方法(转)
摘自:http://www.cnblogs.com/dolphin0520/p/3933551.html#undefined 在前面一篇文章中提到,对Vector.ArrayList在迭代的时候如果同 ...
- (转)ConcurrentModificationException异常原因和解决方法
原文地址: http://www.cnblogs.com/dolphin0520/p/3933551.html 一.ConcurrentModificationException异常出现的原因 先看下 ...
- ConcurrentModificationException异常原因和解决方法
一.ConcurrentModificationException异常出现的原因 先看下面这段代码: public class Test { public static void main(Strin ...
随机推荐
- 【原】Java学习笔记002 - JAVA SE编码规范
/* * 编码规范: * 1.所有的命名遵循"见名知意"的原则 * 2.所有的命名不允许使用汉字或拼音 * 3.Java的工程命名建议使用小写,比如:oa.crm.cms... * ...
- 6.2Python数据处理篇之pandas学习系列(二)Series数据类型
目录 目录 (一)Series的组成 (二)Series的创建 1.从标量中创建Series数据 2.从列表中创建Series数据 3.从字典中创建Series数据 4.从ndarry中创建Serie ...
- SQLServer之修改FOREIGN KEY约束
使用SSMS数据库管理工具修改FOREIGN KEY约束 1.连接数据库,选择数据表->右键点击->选择设计(或者展开键,选择要修改的外键,右键点击,选择修改,后面修改步骤相同). 2.在 ...
- PSQLException: FATAL: no pg_hba.conf entry for host "127.0.0.1", user "ambari", database "ambari", SSL off
On your Postgres server, you will need to update your pg_hba.conf file to allow access for the ambar ...
- ESP8266天线问题
http://www.icxbk.com/ask/detail/28832.html 目前市面上常见的外接天线包括 1.FPC天线,就是一小块柔性PCB,上面走一个铜线,下方不覆铜,然后一般带一个贴纸 ...
- sub2ind函数
sub2ind函数是MATLAB中对矩阵索引号检索的函数,用法如下: >> A=rand(5) A = 0.6850 0.0867 0.2290 0.1006 0. ...
- STM32F103/429串口IAP+Ymodem升级
起因: 串口IAP升级在正点原子的例程中有讲解,正点原子的方法是:在RAM中开辟一个120K的数据空间,用来存放bin文件,bin文件通过串口一次性发送到单片机,然后再实现程序的跳转.但是这种方法在实 ...
- JavaWeb工程中web.xml基本配置(转载学习)
一.理论准备 先说下我记得xml规则,必须有且只有一个根节点,大小写敏感,标签不嵌套,必须配对. web.xml是不是必须的呢?不是的,只要你不用到里面的配置信息就好了,不过在大型web工程下使用该文 ...
- 微信小程序中用户登录和登录态维护
提供用户登录以及维护用户的登录状态,是一个拥有用户系统的软件应用普遍需要做的事情.像微信这样的一个社交平台,如果做一个小程序应用,我们可能很少会去做一个完全脱离和舍弃连接用户信息的纯工具软件. 让用户 ...
- 通过sort()方法实现升序和降序排列
数组 升序:Arrays.sort(arr); 降序: 方法一:Arrays.sort(arr,Collections.reverseOrder()); 方法二: package com.yh.sor ...