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

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

问题描述

请考虑以下代码段:

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

这将产生输出:

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

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

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

list-iterator是 fail-fast :如果在创建Iterator之后的任何时间对列表进行结构修改,则除了通过list-iterator自己的remove方法,列表迭代器将抛出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?

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\n",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天全站免登陆