遍历集合的时候对集合进行了修改,例如添加、删除元素,就会抛这个异常。
产生这个异常的例子:
使用增强for遍历ArrayList,调用list的remove删除元素。
import java.util.ArrayList;
import java.util.List;
public class ConcurrentModificationExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
// 使用增强for循环遍历列表
for (String item : list) {
if (item.equals("A")) { // remove B不会报错,A,C会报错
// 尝试在遍历期间移除元素
list.remove(item); // 将引发ConcurrentModificationException
}
}
}
}
增强for循环底层其实也是用的Iterator
。
//上述源码编译后的字节码
Iterator var2 = list.iterator();
while(var2.hasNext()) {
String s = (String)var2.next();
if (s.equals("a")) {
list.remove(s);
}
}
原理:
以ArrayList
为例。
增强for循环底层其实也是用的Iterator
。
底层是有个modcount + checkForCmodificatioException()函数,每次调用next的时候,都会调用这个函数,判断一下count是不是预期的值,不是的话就抛异常。
下面ArrayList
使用的Iterator
源码可以看见。
期望的值是在创建迭代器的时候设置的(上面截图第一个红框可以看见
),会被设置成modCount,list的remove方法,会将modCount修改(见下图,可以发现这里并没有修改exceptedModCount的值),导致modcount和期望的值不一致,所以list.remove会报错。
神奇的是,删除第二个元素不报错(不信你可以试试,删除B,是不会报错的
)。是因为hasNext方法里面是通过cursor != size 去判断是否有下一个元素存在的,删除第二个元素之后,size会减1,等于2,cusor指向的是下一个可用的位置,值也是2,所以hasNext返回false,不会再调用next函数。
Interator为什么调用remove方法的时候,就不报错了?
看第一幅截图,最后remove方法,Interator的remove会将exceptedModCount设置成modCount,这样checkModificationException函数里面这两不就相等了吗?
普通for循环不存在这个异常,因为它不会使用Interator。删除元素可能会导致漏删。
解决方法:
1.使用Interator遍历集合,使用remove方法安全的删除元素
2.使用并发安全的容器。例如ConcurrentHashMap、CopyOnWriteList等
3.SYnchronized或者ReentrantLock显示控制
fail-fast: 程序执行的过程中,出现了错误,就立即停止或者抛异常,而不是继续运行。
参考:
https://juejin.cn/post/7255224807322058810