java.util.ConcurrentModificationException

来源:互联网 发布:usb转ttl怎么接单片机 编辑:程序博客网 时间:2024/05/22 15:10

前言:这已经不是我碰到的第一个bug了···写了一年多的java了,从一无所知慢慢走过来。觉得让自己成长最快的就是这些时不时出现的错误和bug了。现在觉得很有必要将自己遇见的bug,以及出现的bug的原因好好理解并记录了。不能再以简单的解决bug为要求了。顺便也发布到平台上来和大家交流分享。



java.util.ConcurrentModificationException<=========>我叫它:多线程映射索引同步异常(这个命名只是为了让自己念着能第一时间想起这是什么bug。命名无任何考据,诸位见笑了···)


出现bug的代码片段:

/**对用户容器进行遍历,返回一个没有权限为5的用户对象的容器*/

Iterator<UserPo> t = listU.iterator();
while(t.hasNext()){
UserPo ss = t.next();
if(ss.getPower() == 5){
this.ruv.setTeacher(ss);
listU.remove(ss);
}
}


通过自己收集的资料和网上一起其他的解释,出现这个bug的原因是:Iterator 是工作在一个独立的线程中,并且拥有一个 mutex 锁。 Iterator 被创建之后会建立一个指向原来对象的单链索引表,(个人理解:也就是上述代码中t对象有一个单链表指向listU对象中的位置,遍历会根据这个表去进行遍历。)Iterator当原来的对象数量发生变化时,这个索引表的内容不会同步改变,所以当索引指针往后移动的时候就找不到要迭代的对象(个人理解:我们这里定义ss后面的list对象叫ss1,ss1后面的叫ss2,ss在listU对象的位置是i。当执行了listU.remove(ss)后,在listU中ss对象被删除,listU容器中i位置现在存放的是ss1,i+1的位置存放的是ss2。当进入下次遍历时,Iterator会根据单链表去i+1的位置找ss1,可是此时的i+1的位置存放的是ss2。这与单链表中的记录不一致,因此才会抛出异常),所以按照 fail-fast 原则 Iterator 会马上抛出 java.util.ConcurrentModificationException 异常。
所以 Iterator 在工作的时候是不允许被迭代的对象被改变的。但你可以使用 Iterator 本身的方法 remove() 来删除对象, Iterator.remove() 方法会在删除当前迭代对象的同时维护索引的一致性。


因此解决上述bug的方法之一是:

Iterator<UserPo> t = listU.iterator();
while(t.hasNext()){
UserPo ss = t.next();
if(ss.getPower() == 5){
this.ruv.setTeacher(ss);
t.remove();
}
}


如果使用增强for循环对listU容器进行遍历,我还没找到办法解决它。这是有同样异常的代码片段:

for(UserPo s:listU){
if(s.getPower() == 5){
this.ruv.setTeacher(s);
listU.remove(s);
}
}


原创粉丝点击