概述
ArrayList并发修改问题-ConcurrentModificationException(详解)
有一次用到ArrayList的时候
需要遍历整个ArrayList找到指定元素再删除
于是便产生了下面的代码
public class ListError {
public static void main(String[] args) {
ArrayList<Integer> arrayLists = new ArrayList<>();
arrayLists.add(new Integer(1));
arrayLists.add(new Integer(2));
arrayLists.add(new Integer(3));
arrayLists.add(new Integer(4));
arrayLists.add(new Integer(5));
arrayLists.add(new Integer(6));
arrayLists.add(new Integer(7));
arrayLists.add(new Integer(8));
for (Integer num:arrayLists) {
if(num.equals(new Integer(3))){
arrayLists.remove(new Integer(3));
}
}
}
}
然后就产生了下面的错误:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at study.ListError.main(ListError.java:17)
程序抛出了ConcurrentModificationException并发修改异常 可以看到异常出现在checkForComodification方法中
查看ArraryList源码的checkForComodification方法
final void checkForComodification() {
if (ArrayList.this.modCount != this.expectedModCount) {
throw new ConcurrentModificationException();
}
}
}
观察ArraryList源码中的checkForComodification方法
可以发现
当ArrayList.this.modCount != this.expectedModCount的时候会抛出异常
下面来看为什么会出现这两个值不相等的情况
在这之前,我们需要知道一个小知识
foreach底层也是使用Iterator迭代器进行迭代的
所以,最上面的源码的根本实现就是迭代器也就是这样
public class ListError {
public static void main(String[] args) {
ArrayList<Integer> arrayLists = new ArrayList<>();
arrayLists.add(new Integer(1));
arrayLists.add(new Integer(2));
arrayLists.add(new Integer(3));
arrayLists.add(new Integer(4));
arrayLists.add(new Integer(5));
arrayLists.add(new Integer(6));
arrayLists.add(new Integer(7));
arrayLists.add(new Integer(8));
Iterator<Integer> iterator = arrayLists.iterator();
while(iterator.hasNext()){
Integer num = iterator.next();
if(num.equals(new Integer(3))){
arrayLists.remove();//importance
}
}
}
}
从异常信息可以看到是在next()方法中调用了checkForComodification()
下面是ArraryList中next()方法的源码
public E next() {
this.checkForComodification();
int var1x = this.cursor;
if (var1x >= SubList.this.size) {
throw new NoSuchElementException();
} else {
Object[] var2x = ArrayList.this.elementData;
if (var2 + var1x >= var2x.length) {
throw new ConcurrentModificationException();
} else {
this.cursor = var1x + 1;
return var2x[var2 + (this.lastRet = var1x)];
}
}
}
可见异常产生在 this.checkForComodification();这一句
下面我们依据自己的程序的源码来一步步看ArraryList源码的实现
debug程序在Iterator iterator = arrayLists.iterator();
进入到ArrayList
public Iterator<E> iterator() {
return new ArrayList.Itr();
}
返回的是一个指向Itr的引用
下面是ArraryList的Itr类
private class Itr implements Iterator<E> {
int cursor;
int lastRet;
int expectedModCount;
private Itr() {
this.lastRet = -1;
this.expectedModCount = ArrayList.this.modCount;
}
public boolean hasNext() {
return this.cursor != ArrayList.this.size;
}
public E next() {
this.checkForComodification();
int var1 = this.cursor;
if (var1 >= ArrayList.this.size) {
throw new NoSuchElementException();
} else {
Object[] var2 = ArrayList.this.elementData;
if (var1 >= var2.length) {
throw new ConcurrentModificationException();
} else {
this.cursor = var1 + 1;
return var2[this.lastRet = var1];
}
}
}
public void remove() {
if (this.lastRet < 0) {
throw new IllegalStateException();
} else {
this.checkForComodification();
try {
ArrayList.this.remove(this.lastRet);
this.cursor = this.lastRet;
this.lastRet = -1;
this.expectedModCount = ArrayList.this.modCount;
} catch (IndexOutOfBoundsException var2) {
throw new ConcurrentModificationException();
}
}
}
public void forEachRemaining(Consumer<? super E> var1) {
Objects.requireNonNull(var1);
int var2 = ArrayList.this.size;
int var3 = this.cursor;
if (var3 < var2) {
Object[] var4 = ArrayList.this.elementData;
if (var3 >= var4.length) {
throw new ConcurrentModificationException();
} else {
while(var3 != var2 && ArrayList.this.modCount == this.expectedModCount) {
var1.accept(var4[var3++]);
}
this.cursor = var3;
this.lastRet = var3 - 1;
this.checkForComodification();
}
}
}
final void checkForComodification() {
if (ArrayList.this.modCount != this.expectedModCount) {
throw new ConcurrentModificationException();
}
}
}
首先我们来看三个变量
lastRet:上一个已经访问过的元素的索引
expectionModCount:修改次数的期望值
cursor:下一个要访问的元素的索引
此时lastRet=-1
cursor=0(初始值)
expectionModCount和modCount相等
可以发现,在调用add方法后modCount的值将会增加
在程序中调用了8次
即现在expectionModCount=modCount=8,
当lastRet=2, cursor=3时
也就是当下一个要被访问的元素是需要删除的元素,
调用next()方法获取这个元素
执行
this.cursor = var1 + 1;
return var2[this.lastRet = var1];
使得lastRet=3,cursor=4
然后程序判断该元素是否是需要被删除的
条件为真
调用arrayLists中的remove()方法
在源代码中观察arrayLists中的remove方法
public boolean remove(Object var1) {
int var2;
if (var1 == null) {
for(var2 = 0; var2 < this.size; ++var2) {
if (this.elementData[var2] == null) {
this.fastRemove(var2);
return true;
}
}
} else {
for(var2 = 0; var2 < this.size; ++var2) {
if (var1.equals(this.elementData[var2])) {
this.fastRemove(var2);
return true;
}
}
}
return false;
}
this.fastRemove(var2);
发现最终调用的是fastRemove方法
private void fastRemove(int var1) {
++this.modCount;
int var2 = this.size - var1 - 1;
if (var2 > 0) {
System.arraycopy(this.elementData, var1 + 1, this.elementData, var1, var2);
}
this.elementData[--this.size] = null;
}
会发现modeCount的值发生了改变
现在modeCount=9而expectionModCount=8
虽然删除了元素
但是当再次调用hasNext()方法时需要调用 this.checkForComodification();
这时modeCount=9 != expectionModCount=8
抛出异常ConcurrentModificationException
终
于
找
出
问
题
所
在
了
下面问题来了
如何修改
细心地读者可能发现了
在Itr类中也有remove方法
public void remove() {
if (this.lastRet < 0) {
throw new IllegalStateException();
} else {
this.checkForComodification();
try {
ArrayList.this.remove(this.lastRet);
this.cursor = this.lastRet;
this.lastRet = -1;
this.expectedModCount = ArrayList.this.modCount;
} catch (IndexOutOfBoundsException var2) {
throw new ConcurrentModificationException();
}
}
}
this.expectedModCount = ArrayList.this.modCount;
这里面关键的一句已经解决了这个问题
现在modeCount=9 = expectionModCount
修改过的代码如下
public class ListError {
public static void main(String[] args) {
ArrayList<Integer> arrayLists = new ArrayList<>();
arrayLists.add(new Integer(1));
arrayLists.add(new Integer(2));
arrayLists.add(new Integer(3));
arrayLists.add(new Integer(4));
arrayLists.add(new Integer(5));
arrayLists.add(new Integer(6));
arrayLists.add(new Integer(7));
arrayLists.add(new Integer(8));
Iterator<Integer> iterator = arrayLists.iterator();
while(iterator.hasNext()){
Integer num = iterator.next();
if(num.equals(new Integer(3))){
iterator.remove();//importance
}
}
System.out.println(arrayLists.toString());
}
/**
* [1, 2, 4, 5, 6, 7, 8]
*/
}
到这里,问题就已经解决
如果对你有帮助
也欢迎大家关注我的个人vx公众号:哪吒学编程
等你来
最后
以上就是整齐电脑为你收集整理的ArrayList并发修改问题-ConcurrentModificationException(详解)的全部内容,希望文章能够帮你解决ArrayList并发修改问题-ConcurrentModificationException(详解)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复