了解concurrentModificationException和ArrayList的实现

时间:2016-01-06 09:21:56

标签: java arraylist iterator

我尝试通过编写以下代码来重现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);

DEMO

documentation of ArrayList mentioned

以来
  

请注意,无法保证迭代器的快速失败行为   一般来说,不可能做出任何硬性保证   存在不同步的并发修改。

我预计在单线程程序中这样的检测是直截了当的。但程序打印

a
[b]

代替。为什么呢?

4 个答案:

答案 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

我添加了删除代码作为示例。 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();
    }
}

答案 2 :(得分:3)

ArrayList 类中的

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);