关于ConcurrentModificationException异常原因及注意事项

本文详细解析了ConcurrentModificationException异常的原因及常见触发场景,包括遍历过程中修改集合、使用subList方法不当以及在toString操作期间修改Map等问题,并提出了相应的解决建议。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

嗨,大家好!最近在工作中遇到了一个棘手的问题(如题),特此在这里跟大家分享一下,第一次发博客,不足之处大家见谅!

异常出现原因:

ConcurrentModificationException这个异常继承自RuntimeException,见名知意,这个异常一般是在Collection 上进行迭代,检测到对象的并发修改,但不允许这种修改时,抛出此异常,废话不多说,直接上干货,下面是几种出现这种异常的情况:
1、在对集合进行遍历的同时对集合进行修改,例如对Map的遍历(List同样适用),如下代码:

    //注:这是编译后的代码,一般对Map集合遍历的代码都会被编译成这个样子
    Map<String, Integer> map = new HashMap();
    // 省略map的初始化代码
    Iterator i = map.entrySet().iterator();
        while(i.hasNext()) {
            Entry<String, Integer> entry = (Entry)i.next();
            String key = (String)entry.getKey();
            map.remove("a");        //map.add("a",18); ***对集合数据进行修改
            String value = ((Integer)entry.getValue()).toString();
            System.out.println("key =" + key + " value = " + value);
        }

2、使用List的subList方法时要注意了(亲身经历委屈),比如从数据库拿到一批数据,进行复杂业务的处理时,需要对List集合进行分割然后起多线程进行处理。subList方法并不会创建新的list对象,本质上只是给你返回了原List的一小段数据的引用,如下(摘自JDK1.8 ArrayList):

        SubList(AbstractList<E> parent,
                int offset, int fromIndex, int toIndex) {
            this.parent = parent;
            this.parentOffset = fromIndex;
            this.offset = offset + fromIndex;
            this.size = toIndex - fromIndex;
            this.modCount = ArrayList.this.modCount;
        }

这种情况下,在多线程对分割后的List1、List2……..等等进行遍历时,如果同时存在修改操作也会出现此异常。
3、一个隐藏很深的大坑奋斗,在对map进行toString的同时修改map,这也是我这次遇到的情况(公司的框架啊!!!)。下面是场景重现:当我对数据库进行分页查询时,会首先根据查询条件(map)进行符合条件的总条目数的查询;关键点来了,查询之后会有一个finally代码块单独从线程池拿一个线程进行日志的打印,日志打印需要的参数其中就包含map.toString。后边我主线程查询完总条目数后会执行一段逻辑,然后会将分页信息添加到map中(分页查询的pageIndex,pageSize)然后在进行分页查询,这就是问题最关键的时刻,前面对map进行toStirng操作,后边又往里面添加数据,在业务量很大的情况下,抛出异常也是在所难免的。
代码简单例子如下(公司代码保密,只能写一个简单例子):

//进行分页查询的主方法
    public void mainMethod(Map<String,Object> param,int pageIndex,int pageSize){
        int totalCount = queryCount(param); //进行总条目数的查询
        //....
        //一些业务逻辑
        //....
        param.put("pageIndex",pageIndex);   //问题代码
        param.put("pageSize",pageSize);     //问题代码
        // 进行分页查询
    }

    public int queryCount(Map<String,Object> param){
        try {
            //进行数据库查询然后返回总条目数
        }catch (Exception e){

        }finally {
            //在这里从线程池拿线程进行日志的打印
            // logger.warn(........param.toStirng()....);
        }
    }

总结:对集合进行遍历的同时,尽量不要进行其他修改操作,如果真有业务需要,那就要考虑当前集合是否是最适合的集合?是否应该使用java.util.concurrent包下的线程安全的数据结构呢?
注:欢迎大家回复讨论,谢谢!转载请注明出处。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值