Java.util.ConcurrentModificationException

来源:互联网 发布:数据恢复软件正式版 编辑:程序博客网 时间:2024/06/05 03:16

Java.util.ConcurrentModificationException

问题发生情景:

最近正在做数据采集相关工作,当使用gson.toJson时报错,此Crash不是100%复现,多次测试才可能出现:

E/2017-06-08 14:19:58.178 [pool-2-thread-102][CrashHandler] [TID:2676]uncaughtException msg = nullI/2017-06-08 14:19:58.178 [pool-2-thread-102][RunningRadioCrashHelper] [TID:2676] [tagRunningRecord] return false null nullE/2017-06-08 14:19:58.178 [pool-2-thread-102][CrashHandler] [TID:2676]不好,发生了异常 uncaughtException* Throwable : java.util.ConcurrentModificationException    at java.util.ArrayList$ArrayListIterator.next(ArrayList.java:573)    at com.elvis.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:96)    at com.elvis.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:61)    at com.elvis.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:69)    at com.elvis.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:126)    at com.elvis.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:244)    at com.elvis.gson.Gson.toJson(Gson.java:671)    at com.elvis.gson.Gson.toJson(Gson.java:650)    at com.elvis.gson.Gson.toJson(Gson.java:605)    at com.elvis.gson.Gson.toJson(Gson.java:585)    at com.utest.pdm.PDMFileManager.appendPageLoadInfo(PDMFileManager.java:274)    at com.utest.pdm.monitor.pageload.PageLoadMonitor$1$1.run(PageLoadMonitor.java:161)    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)    at java.lang.Thread.run(Thread.java:818)

问题分析与解决:

1.查阅相关资料:

An ConcurrentModificationException is thrown when a Collection is modified and an existing iterator on the Collection is used to modify the Collection as well.—-大致意思是:ConcurrentModificationException抛出的条件是,一个迭代器在迭代集合的时候,集合被修改了

2.分析问题栈:

发现问题根源是at java.util.ArrayList执行next()函数时抛出异常,所以猜测问题的大概原因是Gson对ArrayList对象进行序列化时,ArrayList对象长度进行了更改。跟踪上层代码,代码中我们开了一个新的线程去对对象进行序列化,并且pageLoadInfo对象在父线程中还有更改的可能性。这就是问题的所在:

//保存onPause信息,倒序遍历,赋值给第一个页面(原理和 @addDrawInfo 方法类似)for (int i = pageList.size() - 1; i >= 0; i--) {    final PageLoadInfo pageLoadInfo = pageList.get(i);    if (pageLoadInfo.activityClassName.equals(activityClassName) && pageLoadInfo.objectHashCode.equals(objectHashCode)) {        pageLoadInfo.addPauseInfo(start, end);        ThreadPoolUtil.execute(new Runnable() {            @Override            public void run() {                PDMFileManager.getInstance().appendPageLoadInfo(pageLoadInfo);            }        });        break;    }}

3.问题解决方案:

于是我们针对业务的需求,可以将pageLoadInfo对象移除更改队列,让此对象不能再被更改。修复后的代码:

//保存onPause信息,倒序遍历,赋值给第一个页面(原理和 @addDrawInfo 方法类似)for (int i = pageList.size() - 1; i >= 0; i--) {    final PageLoadInfo pageLoadInfo = pageList.get(i);    if (pageLoadInfo.activityClassName.equals(activityClassName) && pageLoadInfo.objectHashCode.equals(objectHashCode)) {        pageLoadInfo.addPauseInfo(start, end);        //移除页面列表        pageList.remove(pageLoadInfo);        //本地文件保存页面信息        ThreadPoolUtil.execute(new Runnable() {            @Override            public void run() {                PDMFileManager.getInstance().appendPageLoadInfo(pageLoadInfo);            }        });        break;    }}

总结:

Java.util.ConcurrentModificationException异常一定是由于对象在备操作的过程中被更改所导致的,本文所出现的问题是由于多线程同时操作同一对象导致的,查阅网上资料发现:使用List、Set、Map 时通过Iterator进行遍历,并通过集合的remove(Object) 进行删除操作,都可能引起此异常。详细请参考这篇文章: [Java ConcurrentModificationException 异常分析与解决方案]

解决问题的方法也都同一个原理,防止对象被同时操作:

  1.使用Iterator提供的remove方法,用于删除当前元素

  2.建一个集合,记录需要删除的元素,之后统一删除

  3.使用线程安全CopyOnWriteArrayList进行删除操作

  4.不使用Iterator进行遍历,需要注意的是自己保证索引正常

  5.处理数据时,把数据转换为数组再进行操作

阅读全文
0 0