了解concurrentModificationException和ArrayList的实现

St.*_*rio 3 java iterator arraylist

我试着通过编写以下代码来重现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);
Run Code Online (Sandbox Code Playgroud)

DEMO

既然,提到文件ArrayList

请注意,迭代器的故障快速行为无法得到保证,因为一般来说,在存在不同步的并发修改时,不可能做出任何硬性保证.

我预计在单线程程序中,这种检测是直截了当的.但程序打印出来了

a
[b]
Run Code Online (Sandbox Code Playgroud)

代替.为什么?

Gri*_*aub 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);
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

这就是为什么你得到你的输出而不是ConcurrentModificationException.ConcurrentModificationException如果你要在列表中添加另一个值(例如last.add("c")),你会得到的,因为i.hasNext()在第一次迭代之后它将是真的并且i.next()将抛出异常.

  • 是的,问题的关键是i.hasNext()是假的,因此检查共同修改永远不会发生,并且永远不会抛出异常.它是错误的原因是因为就迭代器而言,它已经跨过一个大小为1的列表,所以它完成了 (2认同)

Dav*_*INO 5

如文档中所述:

此类的 iterator 和 listIterator 方法返回的迭代器是快速失败的如果在创建迭代器后的任何时间对列表进行结构修改,除了通过迭代器自己的 remove 或 add 方法外,迭代器将抛出 ConcurrentModificationException

因为您正在使用新语法for(String i : last)遍历列表,所以会为您创建一个迭代器,并且您无法在循环时修改该列表

此异常与多线程无关。同样只使用一个线程,您可以抛出该异常。

内部有一个变量modCount,每次修改列表结构都会增加该变量。首次创建迭代器时,它会将 的值保存modCount在变量中expectedModCount。每个后续修改检查该值expectedModCount是否等于modCount。如果不是,ConcurrentModificationException则抛出一个。

我以添加remove的代码为例。同样的addaddAll和所有其他的方法是修改列表。

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();
    }
}
Run Code Online (Sandbox Code Playgroud)