Java - java.util.ConcurrentModificationException 异常原因及解决方法(一)

简介: Java - java.util.ConcurrentModificationException 异常原因及解决方法(一)

在Java语言中,ArrayList是一个很常用的类,在编程中经常要对ArrayList进行删除操作,在使用remove方法对ArrayList进行删除操作时,报java.util.ConcurrentModificationException异常,下面探讨一下该异常的原因以及解决办法。


import java.util.ArrayList;
import java.util.List;
public class Test {
    public static void main(String[] args) {
        List<Integer> listA=new ArrayList<>();
        listA.add(1);
        listA.add(2);
        listA.add(3);
        listA.add(4);
        listA.add(5);
        listA.add(6);
        for(Integer a:listA){
            if (a==3) {
                listA.remove(3);
            }
        }
    }
}

上述代码在删除value=3的元素时,报java.util.ConcurrentModificationException异常,如下


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 com.zhang.Test.main(Test.java:19)

那么是不是删除每一个元素都会报上面的异常呢?经过测试发现,删除value=5的元素(倒数第二个元素)时,就不会报上面异常,除此之外均会报如上异常。(此处不再一一测试,有兴趣可自己编码测试)

我们通过上述测试发现了规律,既除了删除倒数第二个错误不会异常,删除其他元素均会异常。既然掌握了规律,那么就要从源码层面揭露该异常的原因。首先发现Java的for循环,就是将List对象遍历托管给Iterator,你如果要对list进行增删操作,都必须经过Iterator,否则Iterator遍历时会乱,所以直接对list进行删除时,Iterator会抛出ConcurrentModificationException异常。

其实,每次foreach迭代的时候都有两部操作:

第一步:iterator.hasNext()  // 判断是否有下个元素

public boolean hasNext() {
    return cursor != size;
}

第二步:item = iterator.next()  // 下个元素是什么,并赋值给上面例子中的item变量

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];
}

通过debug调试,我们发现,checkForComodification时返回了异常,异常原因为 modCount != expectedModCount。

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

进一步阅读源码,发现:

1、modCount 时List从new开始,被修改的次数。当List调用Remove等方法时,modCount++

2、expectedModCount是指Iterator现在期望这个list被修改的次数是多少次。是在Iterator初始化的时候将modCount 的值赋给了expectedModCount

那么就解释了为什么会报上述异常:

1、modCount 会随着调用List.remove方法而自动增减,而expectedModCount则不会变化,就导致modCount != expectedModCount。

2、在删除倒数第二个元素后,cursor=size-1,此时size=size-1,导致hasNext方法认为遍历结束。

目录
相关文章
|
Oracle Java 关系型数据库
Java(TM) Platform SE binary 已停止工作”的解决方法
Java(TM) Platform SE binary 已停止工作”的解决方法
2139 141
|
开发工具
java.lang.unsatisfiedlinkerror解决方法
java.lang.unsatisfiedlinkerror解决方法
1741 1
idea启动java服务报错OutOfMemoryError: GC overhead limit exceeded解决方法
idea启动java服务报错OutOfMemoryError: GC overhead limit exceeded解决方法
9649 3
|
Java
【Java】已解决java.util.ConcurrentModificationException异常
【Java】已解决java.util.ConcurrentModificationException异常
1501 0
|
存储 监控 算法
Java中的内存泄漏问题及其解决方法
Java中的内存泄漏问题及其解决方法
|
算法 Java
Java数据结构与算法:冲突解决方法
Java数据结构与算法:冲突解决方法
java.lang.ExceptionInInitializerError异常原因及解决方法总结
java.lang.ExceptionInInitializerError异常原因及解决方法总结
6665 0
|
Java UED
Java中String强转int:一种常见的错误和解决方法
在Java中将非数字字符串转换为整数会导致`NumberFormatException`。要解决这个问题,可以使用`try-catch`捕获异常,正则表达式验证数字格式,或利用异常信息提供错误提示。例如,`Integer.parseInt()`会因遇到非数字字符如`&quot;123abc&quot;`而抛出异常,但通过异常处理或正则`\\d+`可确保安全转换。记得在编程时避免直接强转,以防止程序异常中断。
|
缓存 监控 算法
【Java】Java内存溢出:原因、预防和解决方法
【Java】Java内存溢出:原因、预防和解决方法
1569 2
|
算法 Java 开发者
深入理解死锁的原因、表现形式以及解决方法,对于提高Java并发编程的效率和安全性具有重要意义
【6月更文挑战第10天】本文探讨了Java并发编程中的死锁问题,包括死锁的基本概念、产生原因和解决策略。死锁是因线程间争夺资源导致的互相等待现象,常由互斥、请求与保持、非剥夺和循环等待条件引起。常见死锁场景包括资源请求顺序不一致、循环等待等。解决死锁的方法包括避免嵌套锁、设置锁获取超时、规定锁顺序、检测与恢复死锁,以及使用高级并发工具。理解并防止死锁有助于提升Java并发编程的效率和系统稳定性。
867 0