我在Java中使用一个返回ArrayList的共享库;当我迭代它时,可能会抛出ConcurrentModificationException,我正在寻找100%(?)保证是安全的。我正在考虑下面的事情,我很感激任何意见。
data_list是ArrayList<>从MT库返回。
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;
}
}
假设变量pass
为true
,我该如何操作本地?
答案 0 :(得分:9)
没有安全的方法可以做到这一点。 You should not catch ConcurrentModificationException
此类的迭代器和listIterator方法返回的迭代器是快速失败的:如果在创建迭代器之后的任何时候对列表进行结构修改,除了通过迭代器自己的remove或add方法之外,迭代器将抛出一个ConcurrentModificationException。因此,面对并发修改,迭代器会快速而干净地失败,而不是在未来不确定的时间冒着任意的,非确定性行为的风险。
请注意,迭代器的快速失败行为无法得到保证,因为一般来说,在存在非同步并发修改的情况下,不可能做出任何硬性保证。失败快速迭代器会尽最大努力抛出ConcurrentModificationException。因此,编写依赖于此异常的程序以确保其正确性是错误的:迭代器的快速失败行为应仅用于检测错误。
某些集合,例如HashMap
,even can enter an infinite loop when used this way。这是an explanation of how it happens。
你不应该这样做。没有正确的方法可以做到这一点。
要么你误解了图书馆是如何工作的,要么是你需要用一个有能力的开发人员编写的图书馆来切换你的图书馆。
您使用的是哪个库?
答案 1 :(得分:0)
您没有准确定义安全的含义,也没有指定对列表执行何种修改,但在许多情况下,通过索引手动迭代它可能是可以接受的,即
for (int index = 0; index < data_list.size(); index ++)
local.add(data_list.get(index));
我认为,有四种可能的修改方式,具有不同程度的可接受性:
答案 2 :(得分:0)
你的情况很糟糕,但我认为你的解决方案尽可能合理。新的ArrayList应该进入循环,以便在每次失败后重新开始。实际上,最好的办法就是让你的“尝试”行看起来像:
local = new ArrayList<Something>( data_list );
您不希望ArrayList必须自行扩展,因为当您尝试在列表更改之前获取数据时需要一些时间。这应该设置大小,创建它,并用最少的浪费来填充它。
您可能需要捕获ConcurrentModification以外的内容。你可能会学到很难的方法。或者只是抓住Throwable。
如果你想走极端,在它自己的线程中运行for循环中的代码,所以如果它挂起你可以杀死它并重新启动它。这将需要一些工作。
如果你让“旋转”变得足够大,我认为这会奏效。
答案 3 :(得分:-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) {}
}