簡體   English   中英

了解concurrentModificationException和ArrayList的實現

[英]Understanding concurrentModificationException and implementation of 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);

DEMO

從那以后, 提到了ArrayList文檔

請注意,迭代器的故障快速行為無法得到保證,因為一般來說,在存在不同步的並發修改時,不可能做出任何硬性保證。

我預計在單線程程序中,這種檢測是直截了當的。 但程序打印出來了

a
[b]

代替。 為什么?

您的代碼等同於以下內容:

   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 如果你要在列表中添加另一個值(例如last.add("c") ),你將得到ConcurrentModificationException ,因為在第一次迭代后i.hasNext()將為真, i.next()將拋出異常。

如文檔中所述:

這個類的iterator和listIterator方法返回的迭代器是快速失敗的如果在創建迭代器之后的任何時候對列表進行結構修改,除了通過迭代器自己的remove或add方法之外, 迭代器將拋出 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();
    }
}

ArrayList類中的Itr類具有以下方法

public boolean hasNext() {
            return cursor != size;
        }

 final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }

這里, modCount是結構修改列表的次數。 當我們創建for循環時,將在內部創建一個迭代器,並將expectedModCount初始化為modCount

當列表中只有2個元素並且在刪除一個元素之后, for循環將使用hasNext()方法調用來檢查條件。 因此,將首先滿足條件光標!= size (1!= 1)。 因此,循環不會繼續進行,並且不會拋出ConcurrentModificationException。

但是,當列表中有1,3,4等數量的元素時, for循環將在hasNext()方法調用之后繼續進行。 但是,在for循環中使用next()方法獲取元素時,它將調用checkForComodification()並且條件modCount!= expectedModCount將被滿足。 因此,將拋出異常。

對於兩個值,它不會失敗,因為將退出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);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM