[英]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);
從那以后, 提到了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
。
我添加了刪除代碼作為示例。 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();
}
}
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.