【问题标题】:Why i get ConcurrentModificationException even using an Iterator?为什么即使使用迭代器我也会得到 ConcurrentModificationException?
【发布时间】:2018-10-21 08:42:50
【问题描述】:

我有 3 个服务器和一个发送消息的客户端。我实现了一个 BFT 算法。 所以我有这部分代码

    int tam = 0;

    if (unordered.size() <= maxOrderSize) {
        tam = unordered.size();
    } else {
        tam = maxOrderSize;
    }
    HashMap<String, byte[]> prop = new HashMap<String, byte[]>(tam);

    Iterator<String> it = unordered.keySet().iterator();

    for (int i = 0; i < tam; i++) {
        if (it.hasNext()) {
            String id = it.next();
            prop.put(id, unordered.get(id));
            it.remove();
            unordered.remove(id);

        }
    }

在运行时对象被导入并从我的地图中删除无序。 我还想提一下,定义了无序:

    Map<String, byte[]> unordered = Collections.synchronizedMap(new HashMap<String, byte[]>());

但它突然创建了这个异常:

java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.remove(HashMap.java:1456)
at edu.bft.comm.layer.BatchControl.createOrderMessage(BatchControl.java:123)
at edu.bft.comm.layer.BatchControlTPM.run(BatchControlTPM.java:24)

知道为什么会这样吗?

EDIT1:我试图删除该行:unordered.remove(id);

我得到了那个错误:

java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextNode(HashMap.java:1442)
at java.util.HashMap$KeyIterator.next(HashMap.java:1466)
at edu.bft.comm.layer.BatchControl.createOrderMessage(BatchControl.java:120)
at edu.bft.comm.layer.BatchControlTPM.run(BatchControlTPM.java:24)

EDIT2:我还想提一下,当我迭代 unordered 时,可能会添加一些新对象,而新消息来自客户端。

【问题讨论】:

  • 整个for-loop 似乎是不必要和错误的。 while(it.hasNext(){ String id = it.next(); ...} 不是更有意义吗?
  • 是的,正如 Kevin 建议的那样,它可能应该是一个 while 循环。此外,您真正想要的是对entrySet() 的迭代,而不是keySet()。通过使用错误的迭代器,您需要在prop.put(...) 中进行集合查找。也许通过使用 entrySet 的迭代器,它的remove() 操作可能会起作用。
  • 我更新我的第一篇文章检查完整的代码。和编辑2。谢谢
  • @KevinAnderson 不,我不想删除所有对象,只想删除 tam 中指定的特定数字
  • @MarcusK。检查这个输出pastebin.com/eCFLd3Rn我在for循环之前添加了一个打印,在for循环之后添加了一个打印这个输出已经删除了unordered.remove(id)行;

标签: java dictionary exception iterator


【解决方案1】:

您正在自己修改地图。

删除此行:unordered.remove(id);

根据您的编辑,您在迭代 Map 时正在修改它,所以很明显这是问题所在。

doc 说:

该集合由地图支持,因此对地图的更改会反映在 集合,反之亦然。如果在迭代时修改了地图 超过集合正在进行中(除了通过迭代器自己的删除 操作),迭代的结果是不确定的。

如你所见,HashMap 会抛出一个ConcurrentModificationException

【讨论】:

  • 好的,我删除了那行,我重新开始了我的程序。现在它工作正常。我会等着看它是否会正常结束。谢谢
  • 请检查第一篇文章我更新了发生的事情
  • 我的代码运行没有问题。您是否发布了完整代码?
  • 你的代码中还有`unordered.remove(id);`
  • 检查 EDIT1.. 我描述了删除该行后发生的情况
【解决方案2】:

好吧,在Edit2之后,我觉得换个答案说得通了。

首先:如果你有一个标准的HashMap,它默认不是线程安全的!因此,如果其他线程同时修改它,迭代这样的集合可能总是会导致麻烦。也许添加新项目不会导致异常,也许会。

如果您有可能更改 unordered 的 Map 实现,请使用线程安全的东西,例如 ConcurrentHashMap。如果有人修改它,而另一个线程用迭代器迭代它的元素,这个不会有任何问题 - 迭代器有自己的项目集合,不会直接干扰原始 Map。

如果你不能改变实现,你就有麻烦了……

【讨论】:

  • 现在无序是: Map unordered = Collections.synchronizedMap(new HashMap());你说把它改成 ConcurrentHashMap unordered = new ConcurrentHashMap();
  • 正确。然后重新考虑你的应用程序设计:你有生产者-消费者问题吗?是否每次放入unsorted 都需要推送到prop?如果是这样,您肯定需要更复杂的解决方案...
  • 该项目是关于实现 BFT 算法。从 unordered 中删除的所有消息都添加到 prop,然后我将 prop 发送到其他服务器...
  • 哇,好的,这是一个不错的挑战。我希望你最终能成功地完成你的实现。或者你可能包含一个已经实现 BFT 的现有库。无论您的最终解决方案是什么,它都可能不再使用 Map 界面......
【解决方案3】:

这正是ConcurrentModificationException 阻止的:

例如,如果线程在使用快速失败迭代器迭代集合时直接修改了集合,则迭代器将抛出此异常。

一般来说,当迭代器迭代时,你不能修改集合。 请参阅SO answerthis one

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-09-26
    • 2021-02-07
    • 2016-01-24
    • 2015-10-24
    • 2014-08-10
    • 1970-01-01
    • 2021-01-14
    • 1970-01-01
    相关资源
    最近更新 更多