【Java — 并发修改异常(ConcurrentModificationException)】

  • Post author:
  • Post category:java




概述

在利用迭代器遍历集合的时候,如果尝试对集合结构进行修改,就会抛出并发修改异常,如添加、删除元素,仅进行元素内容的修改不会产生并发修改异常。

示例如下:

public class CoModiDemo {
	public static void main(String[] args) {
		List<String> list = new ArrayList<>();
		list.add("Apple");
		list.add("Peach");
		list.add("Lemon");
		
		Iterator<String> it = list.iterator();
		
		while(it.hasNext()) {
			if(it.next().equals("Lemon")) {
				// 迭代时添加元素
				list.add("Banana");
				// 迭代时删除元素
//				list.remove("Lemon");
			}
		}
		
	}
}

运行上述代码,报异常如下:

Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:911)
	at java.util.ArrayList$Itr.next(ArrayList.java:861)
	at learning.collection.CoModiDemo.main(CoModiDemo.java:17)



源码分析

从错误报告信息中可以看出,错误来源于 ArrayList 类的内部类 Itr 的 next() 方法所调用的 checkForComodification() 方法。

定位到 checkForComodification():

final void checkForComodification() {
      if (modCount != expectedModCount)
          throw new ConcurrentModificationException();
  }

可以看出,抛出异常的原因是变量

modCount

和 变量

expectedModCount

不相等。


  • modCount

    是ArrayList 的成员变量,继承自 AbstractList 类,该变量记录了集合发生结构修改的次数,初始值为零。每当集合调用了 add()、remove() 等方法时,该变量加一,如下:
/***** ArrayList 类的 add() 方法 *****/
private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

private void ensureExplicitCapacity(int minCapacity) {
    // modCount 变量加一
    modCount++;

    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

public boolean add(E e) {
     ensureCapacityInternal(size + 1);  // Increments modCount!!
     elementData[size++] = e;
     return true;
}
 
 /**** ArrayList 类的 remove() 方法 ****/
public E remove(int index) {
     rangeCheck(index);
	// modCount 变量加一
     modCount++;
     E oldValue = elementData(index);

     int numMoved = size - index - 1;
     if (numMoved > 0)
         System.arraycopy(elementData, index+1, elementData, index,
                          numMoved);
     elementData[--size] = null; // clear to let GC do its work

     return oldValue;
}

  • expectedModCount

    是 ArrayList 类的内部类 Itr 的成员变量,表示预期修改次数。当调用 iterator() 方法获取迭代器时,会创建 Itr 的对象,并且把 ArrayList 对象的

    modCount

    值赋给

    expectedModCount

int expectedModCount = modCount;

也就是说,创建的迭代器 Itr 对象时,

expectedModCount

的值就固定了,为创建迭代器的集合的

modCount

值。本文开篇的案例中,由于在迭代中修改了集合的结构,导致其

modCount

值增加,不再与

expectedModCount

值相等,所以调用 next() 方法时,next() 方法调用的 checkForComodification() 方法中抛出错误。



如何避免并发修改异常

  1. 删除元素时使用迭代器提供的 remove() 方法

    使用迭代器提供的 remove() 方法,导致

    modCount

    改变时,会随之更新

    expectedModCount

    ,将更改后的

    modCount

    值赋值给

    expectedModCount

    ,所以不会产生并发修改错误。
public class CoModiDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("Apple");
        list.add("Peach");
        list.add("Lemon");

        System.out.println(list.size());

        Iterator<String> it = list.iterator();

        while(it.hasNext()) {
            if(it.next().equals("Lemon")) {
                it.remove();
            }
        }

        System.out.println(list.size());
    }
}

运行结果:

3
2
  1. 使用 for 循环
public class CoModiDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("Apple");
        list.add("Peach");
        list.add("Lemon");

        System.out.println(list.size());
        
       for(int i = 0; i < list.size(); i++) {
           if(list.get(i).equals("Lemon")) {
               list.add("Watermelon");
           }
       }

        System.out.println(list.size());
    }
}

运行结果:

3
4



版权声明:本文为qq_45312083原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。