为什么在这种情况下没有ConcurrentModificationException与LinkedList迭代器



请考虑以下代码片段:

List<String> list = new LinkedList<>();
list.add("Hello");
list.add("My");
list.add("Son");
for (String s: list){
    if (s.equals("My")) list.remove(s);
    System.out.printf("s=%s, list=%sn",s,list.toString());
}

这将产生输出:

s=你好,

列表=[你好,我的,儿子]
s=我的,列表=[你好,儿子]

很明显,循环只进入了两次,第三个元素"儿子"从未被访问过。从基础库代码来看,迭代器中的 hasNext() 方法似乎不检查并发修改,只检查下一个索引的大小。由于 remove() 调用的大小减少了 1,因此循环不会再次进入,但不会抛出 ConcurrentModificationException。

这似乎与迭代器的合约相矛盾:

列表迭代器

快速失败的:如果在创建迭代器后的任何时间对列表进行结构修改,则除了通过列表迭代器自己的removeadd方法之外,列表迭代器将抛出ConcurrentModificationException。 因此,面对并发修改,迭代器会快速而干净地失败,而不是冒着在未来不确定的时间出现任意、非确定性行为的风险。

这是一个错误吗?同样,迭代器的合约在这里肯定是不遵守的 - 列表的结构在迭代过程中被迭代器以外的其他东西在结构上修改。

阅读类级 Javadoc:

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

从 https://docs.oracle.com/javase/7/docs/api/java/util/ArrayList.html:

请注意,无法保证迭代器的快速故障行为 因为一般来说,不可能做出任何硬性保证 在存在不同步的并发修改的情况下。快速故障 迭代器在尽力而为时抛出 ConcurrentModificationException 基础。因此,编写一个依赖于 关于此异常的正确性:快速故障行为 迭代器应仅用于检测错误。

也就是说,迭代器将尽力引发异常,但不能保证在所有情况下都这样做。

以下是有关快速失败迭代器如何工作以及如何实现的更多链接 - 以防有人感兴趣:

http://www.certpal.com/blogs/2009/09/iterators-fail-fast-vs-fail-safe/

http://javahungry.blogspot.com/2014/04/fail-fast-iterator-vs-fail-safe-iterator-difference-with-example-in-java.html

http://www.javaperformancetuning.com/articles/fastfail2.shtml

这是另一个SO问题,人们试图找出同样的事情:

为什么这段代码不会导致 ConcurrentModificationException?

相关内容

  • 没有找到相关文章

最新更新