问题描述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vBCEkCAp-1664620566645)(C:/Users/19444/AppData/Roaming/Typora/typora-user-images/image-20221001110837376.png)]

通过错误提示,知道了异常出现在checkForComodification这个方法上,异常原因是modCount != expectedModCount
在这里插入图片描述
在网上查询得知,在很多容器中,都有一个变量记录你从结构上修改此容器的次数,叫做modCount,查看ArrayList的add()和remove()方法就可以发现,每次你调用add方法()向容器里面增加了一个元素,或者你调用Remove()方法删除了其中的某个元素,这个值都会增加1。在对集合进行迭代的时候,这个值不能被改变,否则抛出异常ConcurrentModificationException。简单地说就是你在遍历的时候,你自己不会去改变这个值,但是你在遍历的过程中发现这个值被改变了,那只有一种解释,其他人修改了集合导致这个值改变了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-spDrSXQd-1664620566646)(C:/Users/19444/AppData/Roaming/Typora/typora-user-images/image-20221001115507225.png)]
而expectedModCount表示的是list容器被修改的次数的期望值,它的初始值为modCount

出现原因

在这里插入图片描述

我们先根据程序的代码一步一步看ArrayList源码的实现:

首先看ArrayList的iterator()方法,源码如下:

从源码上可以看出iterator()方法返回的是一个指向Itr类型对象的引用,iterator的实现,在AbstractList类中可以中找到Itr类的具体实现

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FDf3KObm-1664620566647)(C:/Users/19444/AppData/Roaming/Typora/typora-user-images/image-20221001172011407.png)]

每次循环都会经过两步操作,第一步是iterator.hasNext() ,判断是否有下一个元素;当我们调用list.Iterator()返回一个Iterator之后,会通过Iterator的hashNext()方法判断是否还有元素未被访问

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hfn3XuUV-1664620566647)(C:/Users/19444/AppData/Roaming/Typora/typora-user-images/image-20221001172303908.png)]

第二步是iterator.next(),判断下一个元素是什么,并且进行赋值操作;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fEEQMrEu-1664620566647)(C:/Users/19444/AppData/Roaming/Typora/typora-user-images/image-20221001172502327.png)]

可以发现,在next方法中会调用checkForComodification方法,在checkForComodification()方法中,判断了modCount和expectedModCount是否相等。如果不相等,就会抛出我们遇到的这个异常。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0vFviHpg-1664620566648)(C:/Users/19444/AppData/Roaming/Typora/typora-user-images/image-20221001182222166.png)]

解决办法

1、new Vector<>()

2、Collections.synchronizedList(new ArrayList<>());

使用的是Collections工具包

3、new CopyOnWriteArrayList<>();

写时复制copyOnWrite容器即写时复制的容器往容器添加元素的时候,不直接往当前容器object[]添加,而是先将当前容器object[]进行copy,复制出一个新的object[] newElements然后向新容器object[] newElements里面添加元素 添加元素后,再将原容器的引用指向新的容器setArray(newElements);
这样的好处是可以对copyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何容器,所以copyOnwrite容器也是一种读写分离的思想,读和写不同的容器

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐