【问题标题】:java.util.ConcurrentModificationException - Androidjava.util.ConcurrentModificationException - Android
【发布时间】:2016-06-16 06:42:23
【问题描述】:

我有时会从以下代码中得到以下异常 人们建议,如果我对迭代器进行 for 循环,它将解决问题。但我不明白如果我没有删除 for 循环内的任何元素而是调用 pendingActions.clear();之后。 请让我知道是否有人遇到过这种情况。 另外,如果我向

private List<Action> pendingActions = Collections.synchronizedList(new ArrayList<>()); 

会解决问题吗?

 private List<Action> pendingActions = new ArrayList<>();

private void runPendingNavigationActions() {
        if (!pendingActions.isEmpty()) {
            for (Action r : pendingActions) {
                r.run();
            }
            pendingActions.clear();
        }
    } 

例外:

at android.app.ActivityThread.performResumeActivity(ActivityThread.java:4156)
    at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:4250)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1839)
    at android.os.Handler.dispatchMessage(Handler.java:102)
    at android.os.Looper.loop(Looper.java:158)
    at android.app.ActivityThread.main(ActivityThread.java:7229)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)
    Caused by: java.util.ConcurrentModificationException
    at java.util.ArrayList$ArrayListIterator.next(ArrayList.java:573)
    at com.app.android.view.fragment.MainFragment.runPendingNavigationActions(MainFragment.java:1355)

【问题讨论】:

  • 是否有任何Action 在其run() 方法中将自己从列表中删除?
  • 不,run 不会从列表中删除,但在某些竞争条件下,以下代码会在循环迭代时执行 /** * 确保仅在 onReusme 和 onPause 之间执行操作,否则在应用程序恢复后暂停它们以运行。 */ private void runNavAction(Action action) { if (!canCommitFragmentTransaction) { pendingNavActions.add(action); } 其他 { action.run(); } }
  • 这个问题你解决了吗?如果没有,请将run() 方法的内容发布到您的Action 类中
  • 尚未修复,但我没有触摸运行方法中的列表
  • @Bulu 如果该代码同时运行,则可以解释问题。为什么该代码在另一个线程中运行?这些Actions 是什么?

标签: java android


【解决方案1】:

您使用了错误的数据结构:Queue 将是更好的选择,特别是为并发修改而设计的。

例如,您可以使用LinkedBlockingQueue。您可以像列表一样添加到此,然后使用添加的操作,例如:

Action r;
while ((r = queue.poll()) != null) {
  r.run();
}

使用Collections.synchronizedList 不会解决您的问题,至少不会解决问题。

使用Collections.synchronizedList() 包装列表时要记住的是,它只为每个单独的方法调用提供互斥,例如直到对pendingActions.get()pendingActions.iterator() 的调用返回。

增强的for循环隐式创建Iterator;但是当你迭代它时,列表不会被锁定,无论它是否来自同步列表。

您的其他线程在此迭代期间正在更改列表,并且在您下次尝试从迭代器获取值时仍会导致 ConcurrentModificationException

现在,您可以通过将迭代包装在 synchronized 块中来使其工作(没有 CME)以及

synchronized (pendingActions) {
  for (Action r : pendingActions) {
    r.run();
  }
  r.clear();
}

这样做的问题是,在迭代列表时,其他线程现在将被阻塞。这可能会导致您的应用出现明显的延迟;也可能不会,具体取决于调用此方法的频率以及操作运行所需的时间。

但是,使用并发队列完全避免了这个问题,并且可能也比同步列表更快,因为非阻塞并发队列不需要使用“全面”同步。

【讨论】:

  • 感谢您的出色回答。我使用了 LinkedBlockingQueue 并且希望可以工作。我从未能够复制该问题,但在生产崩溃日志中看到,并希望在 Prod 中进行最好的测试,并且不应该在使用 ArrayList 替换 LinkedBlockingQueue 时引入任何新问题
猜你喜欢
  • 2020-06-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多