[查异常网]-20160329-Java ConcurrentModificationException异常原因和解决方法

Vue项目实战:el-avatar组件加载本地图片的3种正确姿势(附避坑指南) 本文详细解析了在Vue项目中使用Element UI的el-avatar组件加载本地图片的3种正确方法,包括require动态引入、public目录静态引用和动态URL生成,并提供了实用的避坑指南。特别针对assets目录下图片加载失败的问题,给出了webpack打包策略的深度分析和解决方案,帮助开发者高效实现头像展示功能。 阅读详情

对Vector、ArrayList在迭代的时候如果同时对其进行修改就会抛出java.util.ConcurrentModificationException异常。下面我们就来讨论以下这个异常出现的原因以及解决办法。

  以下是本文目录大纲:

  一.ConcurrentModificationException异常出现的原因

  二.在单线程环境下的解决办法

  三.在多线程环境下的解决方法

  若有不正之处请多多谅解,并欢迎批评指正

一.ConcurrentModificationException异常出现的原因

先看下面这段代码:

public class Test {
    public static void main(String[] args)  {
        ArrayList<Integer> list = new ArrayList<Integer>();
        list.add(2);
        Iterator<Integer> iterator = list.iterator();
        while(iterator.hasNext()){
            Integer integer = iterator.next();
            if(integer==2)
                list.remove(integer);
        }
    }
}
运行结果:

从异常信息可以发现,异常出现在checkForComodification()方法中。

  我们不忙看checkForComodification()方法的具体实现,我们先根据程序的代码一步一步看ArrayList源码的实现:

  首先看ArrayList的iterator()方法的具体实现,查看源码发现在ArrayList的源码中并没有iterator()这个方法,那么很显然这个方法应该是其父类或者实现的接口中的方法,我们在其父类AbstractList中找到了iterator()方法的具体实现,下面是其实现代码:

public Iterator<E> iterator() {
    return new Itr();
}

从这段代码可以看出返回的是一个指向Itr类型对象的引用,我们接着看Itr的具体实现,在AbstractList类中找到了Itr类的具体实现,它是AbstractList的一个成员内部类,下面这段代码是Itr类的所有实现:

private class Itr implements Iterator<E> {
    int cursor = 0;
    int lastRet = -1;
    int expectedModCount = modCount;
    public boolean hasNext() {
           return cursor != size();
    }
    public E next() {
           checkForComodification();
        try {
        E next = get(cursor);
        lastRet = cursor++;
        return next;
        } catch (IndexOutOfBoundsException e) {
        checkForComodification();
        throw new NoSuchElementException();
        }
    }
    public void remove() {
        if (lastRet == -1)
        throw new IllegalStateException();
           checkForComodification();
 
        try {
        AbstractList.this.remove(lastRet);
        if (lastRet < cursor)
            cursor--;
        lastRet = -1;
        expectedModCount = modCount;
        } catch (IndexOutOfBoundsException e) {
        throw new ConcurrentModificationException();
        }
    }
 
    final void checkForComodification() {
        if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
    }
}

首先我们看一下它的几个成员变量:

  cursor:表示下一个要访问的元素的索引,从next()方法的具体实现就可看出

  lastRet:表示上一个访问的元素的索引

  expectedModCount:表示对ArrayList修改次数的期望值,它的初始值为modCount。

  modCount是AbstractList类中的一个成员变量

protected transient int modCount = 0;

该值表示对List的修改次数,查看ArrayList的add()和remove()方法就可以发现,每次调用add()方法或者remove()方法就会对modCount进行加1操作。

  好了,到这里我们再看看上面的程序:

  当调用list.iterator()返回一个Iterator之后,通过Iterator的hashNext()方法判断是否还有元素未被访问,我们看一下hasNext()方法,hashNext()方法的实现很简单:

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

如果下一个访问的元素下标不等于ArrayList的大小,就表示有元素需要访问,这个很容易理解,如果下一个访问元素的下标等于ArrayList的大小,则肯定到达末尾了。

  然后通过Iterator的next()方法获取到下标为0的元素,我们看一下next()方法的具体实现:

public E next() {
    checkForComodification();
 try {
    E next = get(cursor);
    lastRet = cursor++;
    return next;
 } catch (IndexOutOfBoundsException e) {
    checkForComodification();
    throw new NoSuchElementException();
 }
}

这里是非常关键的地方:首先在next()方法中会调用checkForComodification()方法,然后根据cursor的值获取到元素,接着将cursor的值赋给lastRet,并对cursor的值进行加1操作。初始时,cursor为0,lastRet为-1,那么调用一次之后,cursor的值为1,lastRet的值为0。注意此时,modCount为0,expectedModCount也为0。

  接着往下看,程序中判断当前元素的值是否为2,若为2,则调用list.remove()方法来删除该元素。

  我们看一下在ArrayList中的remove()方法做了什么:

public boolean remove(Object o) {
    if (o == null) {
        for (int index = 0; index < size; index++)
            if (elementData[index] == null) {
                fastRemove(index);
                return true;
            }
    } else {
        for (int index = 0; index < size; index++)
            if (o.equals(elementData[index])) {
                fastRemove(index);
                return true;
            }
    }
    return false;
}
 
 
private void fastRemove(int index) {
    modCount++;
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                numMoved);
    elementData[--size] = null; // Let gc do its work
}

通过remove方法删除元素最终是调用的fastRemove()方法,在fastRemove()方法中,首先对modCount进行加1操作(因为对集合修改了一次),然后接下来就是删除元素的操作,最后将size进行减1操作,并将引用置为null以方便垃圾收集器进行回收工作。

  那么注意此时各个变量的值:对于iterator,其expectedModCount为0,cursor的值为1,lastRet的值为0。

  对于list,其modCount为1,size为0。

  接着看程序代码,执行完删除操作后,继续while循环,调用hasNext方法()判断,由于此时cursor为1,而size为0,那么返回true,所以继续执行while循环,然后继续调用iterator的next()方法:

  注意,此时要注意next()方法中的第一句:checkForComodification()。

  在checkForComodification方法中进行的操作是:

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

如果modCount不等于expectedModCount,则抛出ConcurrentModificationException异常。

  很显然,此时modCount为1,而expectedModCount为0,因此程序就抛出了ConcurrentModificationException异常。

  到这里,想必大家应该明白为何上述代码会抛出ConcurrentModificationException异常了。

  关键点就在于:调用list.remove()方法导致modCount和expectedModCount的值不一致。

  注意,像使用for-each进行迭代实际上也会出现这种问题。

二.在单线程环境下的解决办法

既然知道原因了,那么如何解决呢?

  其实很简单,细心的朋友可能发现在Itr类中也给出了一个remove()方法:

public void remove() {
    if (lastRet == -1)
    throw new IllegalStateException();
       checkForComodification();
 
    try {
    AbstractList.this.remove(lastRet);
    if (lastRet < cursor)
        cursor--;
    lastRet = -1;
    expectedModCount = modCount;
    } catch (IndexOutOfBoundsException e) {
    throw new ConcurrentModificationException();
    }
}
 在这个方法中,删除元素实际上调用的就是list.remove()方法,但是它多了一个操作:

expectedModCount = modCount;

因此,在迭代器中如果要删除元素的话,需要调用Itr类的remove方法。

  将上述代码改为下面这样就不会报错了:

public class Test {
    public static void main(String[] args)  {
        ArrayList<Integer> list = new ArrayList<Integer>();
        list.add(2);
        Iterator<Integer> iterator = list.iterator();
        while(iterator.hasNext()){
            Integer integer = iterator.next();
            if(integer==2)
                iterator.remove();   //注意这个地方
        }
    }
}
三.在多线程环境下的解决方法

上面的解决办法在单线程环境下适用,但是在多线程下适用吗?看下面一个例子:

public class Test {
    static ArrayList<Integer> list = new ArrayList<Integer>();
    public static void main(String[] args)  {
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        Thread thread1 = new Thread(){
            public void run() {
                Iterator<Integer> iterator = list.iterator();
                while(iterator.hasNext()){
                    Integer integer = iterator.next();
                    System.out.println(integer);
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
        };
        Thread thread2 = new Thread(){
            public void run() {
                Iterator<Integer> iterator = list.iterator();
                while(iterator.hasNext()){
                    Integer integer = iterator.next();
                    if(integer==2)
                        iterator.remove(); 
                }
            };
        };
        thread1.start();
        thread2.start();
    }
}
运行结果:

有可能有朋友说ArrayList是非线程安全的容器,换成Vector就没问题了,实际上换成Vector还是会出现这种错误。

  原因在于,虽然Vector的方法采用了synchronized进行了同步,但是由于Vector是继承的AbstarctList,因此通过Iterator来访问容器的话,事实上是不需要获取锁就可以访问。那么显然,由于使用iterator对容器进行访问不需要获取锁,在多线程中就会造成当一个线程删除了元素,由于modCount是AbstarctList的成员变量,因此可能会导致在其他线程中modCount和expectedModCount值不等。

  就比如上面的代码中,很显然iterator是线程私有的,

  初始时,线程1和线程2中的modCount、expectedModCount都为0,

  当线程2通过iterator.remove()删除元素时,会修改modCount值为1,并且会修改线程2中的expectedModCount的值为1,

  而此时线程1中的expectedModCount值为0,虽然modCount不是volatile变量,不保证线程1一定看得到线程2修改后的modCount的值,但是也有可能看得到线程2对modCount的修改,这样就有可能导致线程1中比较expectedModCount和modCount不等,而抛出异常。

  因此一般有2种解决办法:

  1)在使用iterator迭代的时候使用synchronized或者Lock进行同步;

  2)使用并发容器CopyOnWriteArrayList代替ArrayList和Vector。




2026.06.15+细节判断+郭熙+(笔记)(郭熙言语高分“十二计”).pdf 立即下载

相关推荐

高校技术转移办公室人员在推进成果转化时,如何快速识别高质量专利?.docx

高校技术转移办公室人员在推进成果转化时,如何快速识别高质量专利?

ConcurrentModificationException集合的并发修改异常(问题解决)

ConcurrentModificationException:并发修改异常 当方法检测到对象并发修改的时候,但是不允许这种修改就抛出此异常 迭代器是依赖与集合而存在的,在遍历迭代器中元素的时候,如果往集合中添加元素,迭代器又不知道你往集合中添加了元素,这时候就会报错。 所以在迭代器遍历的时候,不能去通过集合去修改元素 举例,在集合的java字符串后面添加hello字符串 这是错误代码 package review.ListDemo1; import java.util.ArrayList; import

一纸春秋的博客 301

政府科技管理者如何推动高校科研成果精准对接产业需求?.docx

政府科技管理者如何推动高校科研成果精准对接产业需求?

ConcurrentModificationException 集合并发修改异常 解决

import java.util.ArrayList; import java.util.List; import java.util.ListIterator; /** * 问题? * 有一个集合,如下,请问,我想判断里面有没有"world"这个元素,如果有,我就添加一个"javaee"元素,请写代码实现。 * ...

weixin_34362790的博客 132

Python全栈入门实战:FastAPI+工单台账+Excel导出(含教学手册与源码骨架)

原创教学资源包(VIP专属)。围绕 Python 全栈入门搭建可复现闭环:用 FastAPI 提供工单台账接口,SQLite 落库,原生 HTML/JS 页面完成列表、新增与筛选,再用 openpyxl 导出带中文表头的 Excel。内含中文实战手册与可运行源码骨架,实现均为教学桩,注释标明鉴权、迁移、并发与备份等生产注意点。使用方法:解压后进入 src/ 目录,建议 Python 3.11+ 创建虚拟环境并安装 requirements.txt;先运行 python smoke_test.py 验证创建、列表、更新与导出,再执行 uvicorn app.main:app --reload --host 127.0.0.1 --port 8000,浏览器打开 http://127.0.0.1:8000 即可操作工单并下载 Excel。细节见手册.md 与 src/README_SRC.txt。适合作为成长任务「Python全栈开发资源包」的自制合规 VIP 资源上传。建议售价 9.90 元。数据均为虚构示例,不含刷量、搬运或真实个人信息。

交错并联图腾柱PFC变换器仿真设计(Simulink仿真实现)

内容概要:本文档围绕“交错并联图腾柱PFC变换器”的Simulink仿真实现展开,系统阐述了该高效率功率因数校正(PFC)拓扑的建模、控制策略设计与仿真验证全过程。作为适用于中大功率场合的先进PFC结构,交错并联图腾柱通过降低输入电流纹波、提高功率密度和系统效率,展现出显著优势。文档详细介绍了系统总体架构、主电路参数设计、双闭环控制策略(包括电压外环与电流内环)、PWM调制技术、软启动机制以及仿真结果分析等内容,帮助读者掌握从理论分析到仿真实践的完整开发流程。同时,文档还引入与其他主流变换器拓扑(如LLC、DAB、Vienna整流器等)的对比分析与协同应用场景探讨,体现了其在现代电力电子系统集成化发展中的重要地位。; 适合人群:具备电力电子技术基础知识、熟悉Simulink/Matlab仿真环境,从事电源系统设计、新能源变换器研发等相关工作的研究生、工程师及科研人员(建议具有1年以上相关实践经验)。; 使用场景及目标:① 深入理解交错并联图腾柱PFC变换器的工作原理与高性能控制方法;② 掌握基于Simulink的PFC系统建模与闭环控制器设计流程;③ 通过仿真数据分析系统动态响应与稳态性能,优化控制参数;④ 为后续硬件原型开发提供可靠的理论依据与前期验证手段。; 阅读建议:建议结合文档配套的Simulink仿真模型进行逐步操作,重点聚焦于双闭环控制结构设计与PI参数整定环节,深入理解各模块间的耦合关系。同时,可参考文中提及的Vienna、LLC等其他典型拓扑,开展横向对比研究,全面把握不同类型PFC架构的技术特点与适用边界。对于初学者,建议先夯实Buck/Boost电路基础与PWM控制原理,再进入本主题的深入学习。

java.langNoClassdefFoundError & java.lang.ClassNotFoundException

今天小伙伴遇到了一个问题, 异常信息如下: java.lang.NoClassDefFoundError:Could not initialize class XXX错误原因就是 XXX 里面有一个地方写错了,问题解决后,便想到了下面的问题: NoClassdefFoundError异常类联系到 ClassNotFoundException 异常类,感觉很相似,有什么区别?

记录一点 960

产业园区运营负责人如何利用AI技术精准识别和推荐适合的科技成果?.docx

科易网基于AI模型+行业大数据,在技术转移、成果转化、技术经纪、知识产权、产业创新、科技招商等垂直领域的多样化应用场景,推出科技创新领域的AI+工具与数智化解决方案,推动科技创新与产业创新智能化发展。

并发修改异常ConcurrentModificationException

这是一个在java中经常出现的一类错误,错误的主要原因是集合类中的迭代器去遍历集合的时候,由于之前的操作中集合发生了改变,而迭代器并没有被改变,因此不能再通过集合去操作,否则就会出现该异常。下面简单举例:在一个集合中,里面已存在三个元素分别为“h”,”j”,”k”,需要实现以下操作:判断集合中有无”h”元素,如果有,则添加一个”a”元素。常见出错思路如下:A:创建一个集合,并添加多个元素 B:遍历...

qq9764312的博客 547

储能双向Buck-Boost+模型预测电流控制+电压下垂控制仿真【附参考文献】(Simulink仿真实现)

内容概要:本文详细介绍了基于Simulink平台构建的储能双向Buck-Boost变换器仿真模型,融合模型预测电流控制(MPC)与电压下垂控制策略,旨在精确模拟和优化储能系统在充放电过程中的能量双向流动特性。该模型完整实现了电流内环的模型预测控制,以提升动态响应速度与控制精度,并结合电压外环的下垂控制机制,有效模拟微电网中多储能单元间的功率自主分配行为,增强系统的冗余性与可扩展性。控制架构设计严谨,兼顾瞬态响应与稳态性能,适用于复杂工况下的系统稳定性分析。文档配套提供参考文献,强化了技术方案的理论支撑与学术可信度,为电力电子与新能源系统领域的研究提供了高质量的仿真验证平台。; 适合人群:面向具备电力电子、自动控制理论或新能源系统等相关专业背景,且正在从事科研或工程开发工作的技术人员,尤其适合熟悉Simulink仿真环境的研究生、电气工程师及高校教师。; 使用场景及目标:①研究储能系统在频繁充放电切换过程中的动态稳定性与响应特性;②验证模型预测控制在高动态电力变换器中的控制性能与鲁棒性;③实现多储能单元并联运行下的下垂控制策略,优化系统功率均分能力;④为微电网、直流配电系统及分布式能源系统提供核心控制算法的仿真验证与优化平台。; 阅读建议:建议读者结合所提供的参考文献深入理解控制策略的数学建模与理论推导,同时在Simulink环境中动手复现模型,重点调试模型预测控制的代价函数权重、预测时域以及下垂控制系数等关键参数,观察其对系统性能的影响,并可进一步拓展至多变换器并联、故障穿越、通信延迟等复杂场景的仿真研究。

科技中介服务机构如何借助AI技术降低服务门槛并提升客户粘性.docx

科易网基于AI模型+行业大数据,在技术转移、成果转化、技术经纪、知识产权、产业创新、科技招商等垂直领域的多样化应用场景,推出科技创新领域的AI+工具与数智化解决方案,推动科技创新与产业创新智能化发展。

Java并发编程系列之二十六:ConcurrentModificationException

在多线程程序的编写中,经常能遇到ConcurrentModificationException这个异常,这个异常的意思是指出现了并发修改的情况,为了降低对程序的性能的影响,Java开发者将捕获的这种错误以“善意”的方式进行提醒。这种异常一般在对容器的元素遍历的过程中出现了对容器的写操作(包括增加、修改和删除操作)时出现。仔细阅读源码就知道,使用迭代器遍历元素时由一个计数器,这个计数器就是为“快速失败

rhwayfun专栏 7021

Linux CentOS 8系统离线升级内核版本

CentOS 8操作系统离线场景下升级内核版本

WQwinter的博客 3237

Head First Servlets & JSP 学习笔记 第十一章 —— Web应用部署

jar:java archive(java归档) war:web archive(web归档) war文件只是Web应用结构的一个快照,采用了一种更可移植的压缩形式(它实际上就是一个jar文件)。建立war文件,就是把整个Web应用结构压缩起来(不要Web应用名目录,即从WEB-INF那一级开始压缩)。 在Tomcat容器中,war文件的文件名会成为Web应用的名字。 在web....

aochu7451的博客 174

LabVIEW状态机实战:用STM32+DHT11搭建温湿度监测系统(附完整VI源码)

本文详细介绍了如何利用LabVIEW状态机模式与STM32微控制器、DHT11温湿度传感器构建温湿度监测系统。通过硬件选型、状态机设计、LabVIEW程序实现及系统优化等环节,提供了一套完整的解决方案,并附有VI源码,助力开发者快速实现嵌入式温湿度监测应用。

weixin_42549723的博客 521

保姆级教程:用STM32CubeMX HAL库搞定ESP8266连接,附串口printf调试全流程

本文提供了一份详细的STM32CubeMX HAL库教程,指导开发者如何实现ESP8266无线模块的连接与调试。从开发环境搭建、串口配置到AT指令通信解析,涵盖了物联网开发中的关键步骤和常见问题解决方案,帮助开发者快速掌握STM32与ESP8266的集成技术。

weixin_28273593的博客 504

androidfinish Activity时报ConcurrentModificationException异常并闪退

在项目中用工具类AppManager进行对activity的管理和应用的退出。finishActivity()方法代码: /** * 结束指定类名的Activity */ public void finishActivity(Class<?> cls) { for (Activity activity : activityStack) {

鲁西南小辉的博客 1585

HALCON深度学习模型部署新选择:一份详细的OpenVINO 2021.4 LTS集成与配置避坑指南

本文详细介绍了如何将HALCON深度学习模型与OpenVINO 2021.4 LTS集成,提供环境配置、硬件加速优化及故障排除的实用指南。特别针对Intel独立显卡的AI加速器接口进行优化,帮助机器视觉工程师提升AI推理效率,适用于工业检测等高要求场景。

weixin_42531779的博客 238

多线程并发修改异常(ConcurrentModificationE)产生的原因以及解决方案

并发修改异常一 什么是并发修改异常(ConcurrentModificationException)二并发修改异常(ConcurrentModificationException)产生的原因1 modCount是什么意思?2 expectedModCount是什么意思?3 modCount != expectedModCount的原因三解决并发修改异常的方案1使用 Vector(同步的)为什么使用Vector2 使用Collections.synchronizedListCollections.synchr

weixin_45851575的博客 9124

从零到一:树莓派上的YOLO目标检测实战指南

本文详细介绍了如何在树莓派上部署和优化YOLO目标检测模型,从硬件选型到系统配置,再到模型转换与推理优化。通过对比不同树莓派型号的性能差异,提供实用的配置方案和代码示例,帮助开发者快速实现高效的边缘计算视觉系统。特别推荐使用ONNX格式进行模型转换,以提升在树莓派上的推理效率。

weixin_29046035的博客 1063

C++面试八股文

和静态多态(又称编译期多态,即在系统编译期间就可以确定程序将要执行哪个函数),其中动态多态是通过虚函数实现的,虚函数是类的成员函数,存在存储虚函数指针的表叫做虚函数表,虚函数表是一个存储类成员虚函数的指针,每个指针都指向调用它的地方,当子类调用虚函数时,就会去虚表里面找自己对应的函数指针,从而实现“谁调用、实现谁”从而实现多态。而C++符号表中的符号不是以函数名命名的,称为函数名修饰规则,虽然函数名相同,但是函数参数等其他属性不同,取的符号也不同,所以不会产生查询歧义的问题,使得函数可以重载。

qq_46676829的博客 905

并行流的使用偶现ConcurrentModificationException 异常

问题描述 今天排查线上偶现的问题,查日志是并发修改集合的异常: 问题代码: Map<String, Object> context = ContextUtil.getAll(); req.getLines().parallelStream().forEach(lineNo -> { ContextUtil.putAll(context); baseWave.occupyLine(warehouseCode, lineNo, req.getPlanDate()); });

天然卷的博客 1357

【实验必备】对AffectNet数据集进行人脸对齐的小Demo

本文首先简单介绍了AffectNet数据集,及人脸关键点等背景知识,接着Po出了人脸对齐的代码及效果展示。注:本文代码参考自FG2021的Emotion-GCN的开源代码,具有一定的可靠性,欢迎大家学习!

信小海 2670
上一篇: [查异常网]-说在开始的话
下一篇: [查异常网]-20160330-Java内存溢出异常(OutOfMemoryError)常见分析
查异常网
博客等级 码龄11年 1粉丝 14原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值