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

来源:互联网 发布:c语言pdf下载 编辑:程序博客网 时间:2024/05/19 20:01

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

异常出现原因:

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包下的线程安全的数据结构呢?
注:欢迎大家回复讨论,谢谢!转载请注明出处。

阅读全文
2 0
原创粉丝点击