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() 方法中抛出错误。
如何避免并发修改异常
-
删除元素时使用迭代器提供的 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
- 使用 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 版权协议,转载请附上原文出处链接和本声明。