嗨,大家好!最近在工作中遇到了一个棘手的问题(如题),特此在这里跟大家分享一下,第一次发博客,不足之处大家见谅!
异常出现原因:
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包下的线程安全的数据结构呢?
注:欢迎大家回复讨论,谢谢!转载请注明出处。