List.addAll生成的遍历List是否在Java中产生ConcurrentModificationException?

时间:2018-07-03 13:15:16

标签: java

确保在循环中不修改下面产生的ConcurrentModificationExceptionnewMR下的代码,mkrs在并发环境中是不安全的线程。众所周知List.addAll是由System.arraycopy实现的,它是JVM中的固有方法。由List生成的List.addAll遍历在Java中产生ConcurrentModificationException吗?

ArrayList<Foo> mkrs = mrm.getFooPush();
ArrayList<Foo> newMR = new ArrayList<Foo>(mkrs.size());
newMR.addAll(mkrs);
for (Foo mkr : newMR) {
    if (mkr != null && mkr instanceof PushRecommend) {
        PushRecommend pr = (PushRecommend) mkr;
        recommendList.add(new MayKnowMessage(pr));
    }
}

发布在Android机器上发生的异常堆栈跟踪。请注意,(ProGuard:433)for (Foo mkr : newMR)写入的行。

java.util.ConcurrentModificationException:
java.util.ArrayList$Itr.next(ArrayList.java:837)
NewFriendManager.void loadNewFriendMsg(boolean)(ProGuard:433)
NewFriendManager.void reloadNewFriendMsg()(ProGuard:308)
NewFriendManager.boolean handleMessage(android.os.Message)(ProGuard:144)
android.os.Handler.dispatchMessage(Handler.java:106)
android.os.Looper.loop(Looper.java:232)
android.os.HandlerThread.run(HandlerThread.java:61)

2 个答案:

答案 0 :(得分:1)

ArrayList.addAll的{​​{3}}状态:

  

“如果在操作进行过程中修改了指定的集合,则此操作的行为是不确定的。”

所以考虑一下:

newMR.addAll(mkrs);

addAll方法将必须迭代mkrs集合。

如果在调用mkrsaddAll(mkrs)被另一个线程修改,则如果mkrs的类型不是允许同时迭代和更新的并发集合,则CCME是可能的。

答案 1 :(得分:1)

newMR.addAll(mkrs);遍历mkrs, mrm.getFooPush()。 如果在addAll期间更改了该列表,则会收到该消息。

您需要有一个并发安全列表(在getFooPush内部)或更好的数据结构。就像队列一样,如果消息是在末尾添加的,并且您被过滤掉(在PushRecommend上),则从头开始消耗。

然后无需进行复制。

如前所述,Collections.synchronizedList(list)可以使原始数据结构安全。