我尝试通过编写以下代码来重现ConcurrentModificationException:
List<String> last = new ArrayList<>();
last.add("a");
last.add("b");
for(String i : last){
System.out.println(i);
last.remove(i);
}
System.out.println(last);
自documentation of ArrayList
mentioned
请注意,无法保证迭代器的快速失败行为 一般来说,不可能做出任何硬性保证 存在不同步的并发修改。
我预计在单线程程序中这样的检测是直截了当的。但程序打印
a
[b]
代替。为什么呢?
答案 0 :(得分:6)
您的代码等同于以下内容:
List<String> last = new ArrayList<>();
last.add("a");
last.add("b");
for(Iterator<String> i = last.iterator(); i.hasNext(); ) {
String value = i.next();
System.out.println(value);
last.remove(value);
}
System.out.println(last);
for
循环的流程为:
System.out.println(value); // prints "a"
last.remove(value); // removes "a" from the list
i.hasNext() // exits the loop, since i.hasNext() is false
System.out.println(last); // prints "[b]" - the updated list
这就是为什么你得到你的输出而没有ConcurrentModificationException
。
如果您要在列表中添加其他值(例如ConcurrentModificationException
),您将获得last.add("c")
,因为在第一次迭代后i.hasNext()
将为真,i.next()
将抛出异常。
答案 1 :(得分:3)
如文档中所述:
此类的迭代器和listIterator返回的迭代器 方法快速失败:如果列表在结构上进行了修改 创建迭代器之后的时间,除了通过之外的任何方式 迭代器自己删除或添加方法,迭代器将抛出 的 ConcurrentModificationException的强>
因为您使用新语法for(String i : last)
循环遍历列表,所以为您创建了一个迭代器,而在循环时无法修改列表。
此异常与多线程无关。只使用一个线程就可以抛出异常。
在内部有一个变量modCount
,对于列表结构的每次修改都会递增。首次创建迭代器时,它会将modCount
的值保存在变量expectedModCount
中。每次后续修改都会检查expectedModCount
的值是否等于modCount
。如果不是,则抛出ConcurrentModificationException
。
我添加了删除代码作为示例。 add
,addAll
以及修改列表的所有其他方法也是如此。
public E remove(int index) {
rangeCheck(index);
// Check if a modification should thrown a ConcurrentModificationException
checkForComodification();
E result = parent.remove(parentOffset + index);
this.modCount = parent.modCount;
this.size--;
return result;
}
final void checkForComodification() {
if (expectedModCount != ArrayList.this.modCount)
throw new ConcurrentModificationException();
}
}
答案 2 :(得分:3)
Itr 类具有以下方法
public boolean hasNext() {
return cursor != size;
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
此处, modCount 是结构修改列表的次数。当我们创建for循环时,将在内部创建一个迭代器, expectedModCount 将初始化为 modCount 。
如果列表中只有2个元素,并且在删除一个元素后, for loop 将使用 hasNext()方法调用来检查条件。因此,首先会遇到条件 cursor!= size (1!= 1)。因此,循环不会继续进行,并且不会抛出ConcurrentModificationException。
但是,当列表中有1,3,4等元素时, for循环将在 hasNext()方法调用后继续进行。但是,在 for loop 中使用 next()方法获取元素时,它会调用 checkForComodification()并调整 modCount!= expectedModCount 将得到满足。因此,将抛出异常。
答案 3 :(得分:2)
对于两个值,它不会失败,因为将退出for循环。添加第三个元素,您将获得java.util.ConcurrentModificationException
List<String> last = new ArrayList<>();
last.add("a");
last.add("b");
last.add("c");
for(String i : last){
System.out.println(i);
last.remove(i);
}
System.out.println(last);