深入分析集合并发修改异常(源码分析)java.util.ConcurrentModificationException
来源:互联网 发布:erp软件购买ufsky 编辑:程序博客网 时间:2024/06/05 06:38
Exception in thread "main" java.util.ConcurrentModificationExceptionat java.util.AbstractList$Itr.checkForComodification(Unknown Source)at java.util.AbstractList$Itr.next(Unknown Source)at com.qiuyang.Test.Test.main(Test.java:14)
在学习JAVA的时候,大家都知道在迭代集合时,不能去修改集合,但是WHY? 在查看API集合部分源码时,终于找到了原因,先来个例子:
import java.util.*;public class Test {public static void main(String[] args){ArrayList<String> list=new ArrayList<String>();list.add("张三");list.add("李四");list.add("王五");list.add("赵六");for( Iterator<String> it=list.iterator();it.hasNext();){String name=it.next();if("李四".equals(name)){list.remove(name);//集合操作,改变了集合的版本,但不会改变迭代器的版本}elseSystem.out.print(name+" ");}System.out.println("\n"+list);}}执行结果:
张三 Exception in thread "main" java.util.ConcurrentModificationExceptionat java.util.AbstractList$Itr.checkForComodification(Unknown Source)at java.util.AbstractList$Itr.next(Unknown Source)at com.qiuyang.Test.Test.main(Test.java:14)可以看到,程序打印了张三,然后出现并发修改异常,那这个异常时怎么产生的呢?我们找到AbstractList的源代码,取出其中部分,如下:
在AbstractList类中内部成员有:protected transient int modCount = 0;注:该modCount相当于集合的版本号,集合内容每改变一次,其值就+1。观察下面内部类可以知道,在next()、remove()方法都会去判断集合版本号与迭代器版本号是否一致checkForComodification();内部方法有:public Iterator<E> iterator() {return new Itr(); }还有一个内部类:private class Itr implements Iterator<E> {int cursor = 0;int lastRet = -1;//***创建迭代器时就讲版本号给了迭代器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();throw new NoSuchElementException(); }}public void remove() { if (lastRet == -1)throw new IllegalStateException(); checkForComodification(); try {AbstractList.this.remove(lastRet);if (lastRet < cursor) cursor--;lastRet = -1;expectedModCount = modCount;//迭代器进行删除时,会改变it的版本 } catch (IndexOutOfBoundsException e) {throw new ConcurrentModificationException(); }}//检查迭代器的版本与集合的版本是否一致,不同则抛出异常final void checkForComodification() { if (modCount != expectedModCount)throw new ConcurrentModificationException();} }
可以看出,迭代器的next()方法,每次都会检查版本号是否一致,modCount为集合的版本号,excepectedModCount为迭代器的版本号,当版本号不一致时,则出现并发修改异常。下面具体分析一下执行过程:
该案列中,集合list.size()=4,遍历集合时创建集合迭代器,此时有 expectedModCount = modCount=4,迭代器的cursor=0,
(1)cursor=0,it.next()检查版本号一致,取出“张三”,cursor=cursor+1=1,打印,然后it.hasNext()函数判断cursor!=list.size(),返回true
(2)cursor=1,it.next()检查版本号一致,取出“李四”,cursor=cursor+1=2,进入if语句,移除了“李四”,此时集合内容改变,版本号modCount++,为5.然后it.hasNext()判断cursor(2)!=size(4),true
(3)cursor=2,it.next()检查发现版本号不一致expectedModCount! = modCount,抛出并发修改异常。
若将list.remove(name)改为迭代器的操作it.remove(),则会得到正确的结果如下,这是因为迭代器的remove()方法修改了版本号,如上源代码所示。张三 王五 赵六 [张三, 王五, 赵六]二、下面看一个更有意思的例子,同样为集合的并发修改,却没有抛出异常
import java.util.*;public class Test {public static void main(String[] args){ArrayList<String> list=new ArrayList<String>();list.add("张三");list.add("李四");list.add("王五");list.add("赵六");for( Iterator<String> it=list.iterator();it.hasNext();){String name=it.next();if("王五".equals(name)){list.remove(name);//集合操作,不会改变迭代器的版本,会抛出异常}elseSystem.out.print(name+" ");}System.out.println("\n"+list);}}执行结果为:
张三 李四 [张三, 李四, 赵六]可以看到,程序虽然没有抛出异常,但却并没有遍历完整个集合,意味着迭代器提前停止了,具体分析如下:
(1)cursor=0; it.next()检查版本号一致,取出“张三”,cursor++为1,打印“张三”,然后, it.hasNext()判断cursor(1)!=size(4),true;
(2)cursor=1,it.next()检查版本号一致,取出“李四”,cursor++等于2,打印李四,然后,it.hasNext()判断cursor(2)!=size(4);
(3)cursor=2,it.next()检查版本号一致,取出“王五”,cursor++等于3;进入if语句,list.remove(“王五”),此时,版本号modCount++,且集合大小改变list.size()=3,然后,it.hasNext()判断发现cursor(3)==size(3),返回false,迭代结束,故得到结果:张三李四
(也是因为it.hasNext()返回false退出循环,所以才没有再进入it.next()函数,故没有产生并发修改异常)
若将list.remove(name)语句改为 it.remove(),即用迭代器的操作去删除集合的元素,则会得到正确的结果,如下:张三 李四 赵六 [张三, 李四, 赵六]
总结:在对集合进行迭代时,不要去操作集合(指的是用集合去增删元素),但可以用迭代器去操作集合,it.remove(),对于List集合,还可以用ListIterator进行add()操作;
造成上述现象原因,集合操作会改变集合版本号modCount而不会改变迭代器的版本号excepectedModCount,而迭代器操作则集合版本号和迭代器的版本号都会同步的改变,故不会产生异常。
- 深入分析集合并发修改异常(源码分析)java.util.ConcurrentModificationException
- Java集合之ConcurrentModificationException(并发修改异常)分析
- 【Java源码分析】ConcurrentModificationException并发修改异常分析与解决方案(快速失败与安全失败)
- java.util.ConcurrentModificationException异常分析
- java.util.ConcurrentModificationException异常分析
- java.util.ConcurrentModificationException异常分析
- java.util.ConcurrentModificationException异常分析
- java.util.ConcurrentModificationException异常 分析
- java.util.ConcurrentModificationException:并发修改异常!
- java.util.ConcurrentModificationException 并发修改异常处理
- java.util.ConcurrentModificationException ,遍历集合并同时修改集合,并发造成的异常解决办法
- java.util.ConcurrentModificationException 异常的分析
- java.util.ConcurrentModificationException异常分析及处理
- 关于迭代器的在集合遍历中发生的并发修改异常(java.util.ConcurrentModificationException
- Java并发修改错误ConcurrentModificationException分析
- Java并发修改错误ConcurrentModificationException分析
- Java并发修改错误ConcurrentModificationException分析
- Java并发修改错误ConcurrentModificationException分析
- 外连接
- 3D游戏编程与设计 Week3
- hibernate学习笔记
- 解决Eclipse 项目报错:Unbound classpath container: ‘JRE System Library [JavaSE-1.7]
- linux学习笔记009
- 深入分析集合并发修改异常(源码分析)java.util.ConcurrentModificationException
- http://www.cnblogs.com/aibeibei-beauty/articles/5415864.html
- Android TextureView简易教程
- JS DOM之元素操作
- github 上传项目到指定组织
- 深度学习(2)—网上好资料
- 算法之路(四)----汉诺塔(又称河内之塔)
- shell脚本实现自动创建模拟器、启动模拟器、自动安装apk并进行monkey测试
- mysql57.17更新密码