为什么在这种情况下使用 LinkedList 迭代器没有 ConcurrentModificationException? [英] Why no ConcurrentModificationException in this situation with LinkedList iterator?

查看:32
本文介绍了为什么在这种情况下使用 LinkedList 迭代器没有 ConcurrentModificationException?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑以下代码片段:

Listlist = new LinkedList<>();list.add("你好");list.add("我的");list.add("儿子");for (String s: list){if (s.equals("My")) list.remove(s);System.out.printf("s=%s, list=%s
",s,list.toString());}

这导致输出:

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

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

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

<块引用>

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

这是一个错误吗?同样,迭代器的契约在这里显然是不遵守的——列表的结构在迭代过程中被除迭代器之外的其他东西在结构上修改.

解决方案

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

<块引用>

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

也就是说,迭代器会尽力抛出异常,但不能保证在所有情况下都这样做.

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

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

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

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

Consider the following code snippet:

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=%s
",s,list.toString());
}

This results in output:

s=Hello, list=[Hello, My, Son]
s=My, list=[Hello, Son]

So clearly the loop was only entered twice, and the third element, "Son", never gets visited. From the underlying library code, it looks like what happens is that the hasNext() method in the iterator doesn't check for concurrent modification, only the size against the next index. Since the size has been reduced by 1 by the remove() call, the loop simply doesn't get entered again, but no ConcurrentModificationException is thrown.

This seems to contradict the contract of the iterator:

The list-iterator is fail-fast: if the list is structurally modified at any time after the Iterator is created, in any way except through the list-iterator's own remove or add methods, the list-iterator will throw a ConcurrentModificationException. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.

Is this a bug? Again, the contract of the iterator definitely appears to be disobeyed here - the structure of the list is structurally modified by something other than the iterator in the middle of iteration.

解决方案

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

Note that the fail-fast behavior of an iterator cannot be guaranteed as it is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast iterators throw ConcurrentModificationException on a best-effort basis. Therefore, it would be wrong to write a program that depended on this exception for its correctness: the fail-fast behavior of iterators should be used only to detect bugs.

That is, the iterator will try its best to throw exception, but isn't guaranteed to do so in all cases.

Here are some more links on how fail fast iterators works and how the are implemented - in case someone will be interested:

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

And here is another SO question where people trying to find out the same thing:

Why isn't this code causing a ConcurrentModificationException?

这篇关于为什么在这种情况下使用 LinkedList 迭代器没有 ConcurrentModificationException?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆