尝试更新全局变量时java.util.ConcurrentModificationException

时间:2014-05-13 09:10:42

标签: java android multithreading

我在方法中有以下代码(Utils.currentPrinters是一个静态JsonObject):

    JsonObject printers = Utils.currentPrinters;

    for (Map.Entry<String, ?> entry : Utils.currentPrinters.entrySet()) {

        String key = entry.getKey();

        JsonObject printer = (JsonObject) entry.getValue();

        if (...) {
            printers.remove(key);
        }
    }

    Utils.currentPrinters = printers;

有两个线程实际上调用此方法。如果它只是一个,那就完全没有问题了。但是在使用两个线程运行几秒后,它会崩溃并出现以下异常:

 05-13 12:01:43.168    5739-5739/com.wasp.pos.app E/AndroidRuntime﹕ FATAL EXCEPTION: main
java.util.ConcurrentModificationException
        at com.google.gson.internal.LinkedTreeMap$LinkedTreeMapIterator.nextNode(LinkedTreeMap.java:541)
        at com.google.gson.internal.LinkedTreeMap$EntrySet$1.next(LinkedTreeMap.java:565)
        at com.google.gson.internal.LinkedTreeMap$EntrySet$1.next(LinkedTreeMap.java:563)
        at com.wasp.pos.app.server.PrintersFinder.clearPrinters(PrintersFinder.java:184)
        at com.wasp.pos.app.server.PrintersFinder.access$000(PrintersFinder.java:30)
        at com.wasp.pos.app.server.PrintersFinder$1UpdateListThread.run(PrintersFinder.java:124)
        at android.os.Handler.handleCallback(Handler.java:615)
        at android.os.Handler.dispatchMessage(Handler.java:92)
        at android.os.Looper.loop(Looper.java:153)
        at android.app.ActivityThread.main(ActivityThread.java:4987)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:511)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:821)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:584)
        at dalvik.system.NativeStart.main(Native Method)

我无法找到解决此问题的任何内容,ConcurrentMap或synchronized(打印机)没有修复它。请帮忙。

3 个答案:

答案 0 :(得分:2)

在对它们进行迭代(printers.remove(key);)时,无法从集合中删除(for (Map.Entry<String, ?> entry : Utils.currentPrinters.entrySet()))项。

实现目标的最佳方法是收集您要删除的所有项目的列表,并在最后删除它们。

    List<String> remove = new ArrayList<String>();
    for (Map.Entry<String, ?> entry : Utils.currentPrinters.entrySet()) {

        String key = entry.getKey();

        if (...) {
            remove.add(key);
        }
    }
    // Remove the ones I don't want.
    for(String r:remove) {
        printers.remove(r);
    }

完成后,您仍然需要使用synchronizedConcurrentMap

答案 1 :(得分:2)

如上所述,请勿从正在迭代的集合中删除。代替:

Set<String> removed = new TreeSet<String>();
for (Map.Entry<String, ?> entry : Utils.currentPrinters.entrySet()) {

    String key = entry.getKey();

    JsonObject printer = (JsonObject) entry.getValue();

    if (...) {
        removed.add(key);
    }
}
for (String key : removed)
    Utils.currentPrinters.remove(key);

另外两件事:

  • 原始代码中的临时变量“printers”没有任何变化,因为它引用了同一个对象。除非出于某种原因,代码对你来说更清楚......

  • 如果多个线程访问此方法......不要!他们正在做同样的事情,你将从中获益。同步整个块。

答案 2 :(得分:2)

我建议您使用Iterator,它允许您在迭代列表时删除对象,而不会抛出ConcurrentModification异常。