5

我在 Java 中使用返回 ArrayList 的共享库;当我迭代它时,可能会抛出 ConcurrentModificationException ,我正在寻找 100% (?) 保证是安全的。我正在考虑类似下面的内容,我将不胜感激。

data_list 是从 MT 库返回的 ArrayList<>。

boolean pass = true;

ArrayList<Something> local = new ArrayList<Something>(256);

for (int spin=0; spin<10; ++spin)
{
  try {
    local.addAll(data_list);
  }
  catch (java.util.ConcurrentModificationException ce) {
    pass = false;
  }
  finally {
    if (pass) break;
    pass = true;
  }
}

假设变量passtrue,我应该如何在本地操作?

4

4 回答 4

9

没有安全的方法可以做到这一点。 你不应该抓住ConcurrentModificationException

此类的 iterator 和 listIterator 方法返回的迭代器是快速失败的:如果在创建迭代器后的任何时间对列表进行结构修改,除了通过迭代器自己的 remove 或 add 方法之外的任何方式,迭代器将抛出 ConcurrentModificationException。因此,面对并发修改,迭代器快速而干净地失败,而不是在未来不确定的时间冒任意的、非确定性的行为。

请注意,不能保证迭代器的快速失败行为,因为一般来说,在存在不同步的并发修改的情况下,不可能做出任何硬保证。快速失败的迭代器会尽最大努力抛出 ConcurrentModificationException。因此,编写一个依赖于这个异常的正确性的程序是错误的:迭代器的快速失败行为应该只用于检测错误。

某些集合,例如HashMap甚至可以在以这种方式使用时进入无限循环。这是它如何发生的解释

不应该这样做。没有正确的方法可以做到这一点。

要么你误解了库的工作原理,要么你需要用一个有能力的开发人员编写的库来切换你的库。

你在用什么图书馆?

于 2013-07-01T19:00:03.703 回答
0

您的情况很糟糕,但我认为您的解决方案尽可能合理。新的 ArrayList 应该进入循环,以便您在每次失败后重新开始。实际上,最好的办法可能是让您的“尝试”行看起来像:

local = new ArrayList<Something>( data_list );

您不希望您的 ArrayList 必须自行扩展,因为当您在列表更改之前尝试获取数据时,这将需要一些时间。这应该设置大小,创建它,并以最少的精力填充它。

您可能需要捕获 ConcurrentModification 以外的内容。你可能会学到什么困难的方法。或者只是抓住 Throwable。

如果你想走极端,在它自己的线程中运行 for 循环内的代码,这样如果它确实挂起,你可以杀死它并重新启动它。这将需要一些工作。

我认为这会奏效,如果你让“旋转”变得足够大。

于 2013-07-01T21:12:10.160 回答
0

您没有准确定义安全的含义,也没有指定对列表执行哪种修改,但在许多情况下,通过索引手动迭代它可能是可以接受的,即

for (int index = 0; index < data_list.size(); index ++)
    local.add(data_list.get(index));

在我看来,有四种可能的修改方式,可接受程度不同:

  • 可以附加新项目。这个解决方案应该适用于这种情况,只要列表没有增长到足以触发支持列表扩展(并且这应该以指数减少的频率发生,如果发生重试应该保证最终成功)。
  • 现有项目可能会被修改。此解决方案可能不会在任何给定时间呈现列表内容的一致视图,但可以保证提供代表列表中项目的可用列表,这可能是可接受的,具体取决于您的定义“安全的”。
  • 物品可能会被移除。这个解决方案很有可能会因 IndexOutOfBoundsException 而失败,并且与正在修改的项目相同的警告将适用于一致性。
  • 项目可以插入到列表的中间。与正在修改的项目相同的警告将适用,并且还存在获得重复值的危险。附加案例的支持数组扩展问题也将适用。
于 2013-07-01T18:54:18.413 回答
-2

我没有任何根本性的变化,但我认为代码可以简化一点:

ArrayList<Something> local = new ArrayList<Something>(256);

for (int spin=0; spin<10; ++spin)
{
  try {
    local.addAll(data_list);
    break;
  }
  catch (java.util.ConcurrentModificationException ce) {}
}
于 2013-07-01T18:54:16.407 回答