【问题标题】:Why am i getting ConcurrentModificationException on this unmodifiableSet?为什么我在这个 unmodifiableSet 上得到 ConcurrentModificationException?
【发布时间】:2018-11-11 01:54:21
【问题描述】:

我在 for 循环开始的那一行收到了 java.util.ConcurrentModificationException(参见代码中的注释)。

为什么我在这个 unmodifiableSet 上得到 ConcurrentModificationException?

final Set<Port> portSet = Collections.unmodifiableSet(node.getOpenPorts());
if (!portSet.isEmpty()) {
    StringBuilder tmpSb = new StringBuilder();
    for (Port pp : portSet) { // <------- exception happening here
        tmpSb.append(pp.getNum()).append("  ");
    }
}

我从未见过这种情况,但我收到了来自 Google 的崩溃报告。

【问题讨论】:

  • node.getOpenPorts()返回的Set可以被其他代码(不一定是你自己的代码)修改吗?
  • 好的。我想我对Collections.unmodifiableSet 复制该集合的假设是错误的。我猜它只是包装并防止添加/删除?
  • 是的,Collections.unmodifiableXXX 方法都包装了给定的集合。这些包装器委托给底层集合进行读取操作,但抛出UnsupportedOperationException 进行写入操作。

标签: java android concurrency


【解决方案1】:

一定有什么东西在修改底层集合;即node.getOpenPorts()返回的集合。

您可以复制它,而不是使用“不可修改”的包装器来包装集合。

    final Set<Port> portSet = new HashSet<>(node.getOpenPorts());

但正如评论者 (@Slaw) 指出的那样,这只是将迭代移动到构造函数中,您仍然会获得 CCME。

唯一真正的解决方案是:

  • 将节点类的实现更改为对端口列表使用并发集类,如果在迭代时集合发生突变,则不会抛出 CCME。

  • 更改节点类的实现以返回端口列表的副本。使用一些内部锁定来处理复制时更新的竞争条件。

  • 在代码周围放置一个 try/catch 并在获得 CCME 时重复该操作


我从未见过这种情况,但我收到了来自 Google 的崩溃报告。

是的。只有在打开端口列表更改时执行此代码才会出现问题

【讨论】:

  • 这不只是将迭代移动到HashSet内部吗?根据 OP 的代码,复制行为似乎同样可能抛出 ConcurrentModificationException
  • 我将此标记为答案,因为我需要做的是用 ConcurrentSkipListSet 替换我的 TreeSet。不需要Collections.unmodifiableSet。谢谢。
猜你喜欢
  • 2023-01-12
  • 1970-01-01
  • 2021-04-11
  • 1970-01-01
  • 1970-01-01
  • 2014-01-29
  • 2016-11-25
  • 2018-07-29
  • 1970-01-01
相关资源
最近更新 更多