1.何为并发修改异常
首先我们需要看一段代码,如下新建Demo类:
public class Demo {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<String>();
list.add("I");
list.add("love");
list.add("java");
//使用Iterator迭代器对list进行迭代
Iterator it = list.iterator();
while (it.hasNext()) {
if (it.next().equals("java")) {
list.add("very");
}
}
System.out.println(list);
}
}
运行结果:

根据第一行的提示,我们可以看出这是个叫ConcurrentModificationException的异常,翻译过来就是并发修改异常,很好,现在我们去java帮助文档查看该异常的官方描述,如下:

看官方的描述似乎还是一头雾水?好吧,通俗的来讲有一块地种了些庄稼,现在有个人过来施肥,他可以根据庄稼的数量计算带多少肥料正好。但现在又冒出来个种地的,施肥的人需要和他一块工作,那么施肥的人该带多少肥料呢?
这里还要求我们注意,就算是一个人如果不好好按照要求种地(乱调方法),也会导致该异常。
弄明白这个异常什么意思后,现在我们需要从源码的角度去搞清楚这个异常是怎么触发的。
2.源码
根据异常的提示信息我们可以看到在ArrayList下有一个Itr的内部类,该内部类中的checkForComodification()方法出了问题,现在我们把相关代码拿过来,同时删去我们暂时不需要的源代码。
public class ArrayList<E> extends AbstractList<E> implements List<E>{
private static final int DEFAULT_CAPACITY = 10;//生成的ArrayList对象默认长度
private static final Object[] EMPTY_ELEMENTDATA = {};//空的Object数组
private transient Object[] elementData;//装ArrayList元素的数组
private int size;//ArrayList的元素数量
//因为ArrayList实现了List接口,所以要实现List中的Iterator方法和add方法
//Iterator()方法
public Iterator<E> iterator() {
return new Itr();
}
//add方法
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
//add方法又调用了ensureCapacityInternal()方法
private void ensureCapacityInternal(int minCapacity) {
if (elementData == EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
//ensureCapacityInternal()又调用了ensureExplicitCapacity()方法
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
//Itr内部类
private class Itr implements Iterator<E> {
int expectedModCount = modCount;
//checkForComodification()方法
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
@SuppressWarnings("unchecked")
//next()方法
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];
}
}
}
解释:ArrayList实现了List接口,要实现List中的Iterator方法和add方法,所以用Iterator方法进行迭代时,我们要用ArrayList类中重写的Iterator()方法进行迭代。进入Iterator()方法,它返回了一个Itr内部类的对象return new Itr();,接下来,我们来到Itr内部类,根据异常的信息提示,是Itr内部类的next()出了问题,来到next()方法,第一句代码便是调用了方法checkForComodification();根据异常提示,也确实是这个方法最终导致了并发修改异常,我们来到该方法,代码如下:
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
如果modCount不等于expectedModCount,则抛出并发修改异常。
首先我们需要知道这两个值什么意思:
modCount:实际修改次数
expectedModCount:预期修改次数
我们来到Itr内部类,该类第一句代码就为int expectedModCount = modCount;所以一开始实际修改次数和预期修改次数是相等的,而modCount来自于ArrayList的父类AbstractList,在AbstractList中,我们可以找到这样一句代码protected transient int modCount = 0;。所以expectedModCount 和modCount初始值都为0,
来到main方法,当我们调用hasNext()方法后再调用next()方法后,一切问题都没有,但是当我们进行equals判断为true调用add()方法后,就有问题了,进入add()方法,开始一句是调用了ensureCapacityInternal()方法,这个方法又调用了ensureExplicitCapacity()方法,在这里我们看见了一句modCount++;也就是说当执行完一次add()方法后modCount变化了,但是expectedModCount却没有变化,所以当再次调用next(),next()又调用checkForComodification()方法后就会发现modCount !=expectedModCount,然后抛出异常。
3.解决方法
使用for循环:
public class Demo {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<String>();
list.add("I");
list.add("love");
list.add("java");
for(int i=0;i<list.size();i++){
String s=list.get(i);
if(s.equals("love")){
list.add("very much");
}
}
System.out.println(list);
}
}
运行结果:

本文深入解析并发修改异常(ConcurrentModificationException),通过示例代码详细阐述其触发原因,包括实际修改次数与预期修改次数不符的情况,并提供解决方案。
501

被折叠的 条评论
为什么被折叠?



