![](/img/trans.png)
[英]Why this code doesn't throw ConcurrentModificationException when multiple threads work on same arraylist at the same time using iterator
[英]Why doesn't this code throw a ConcurrentModificationException?
為什么這段代碼不拋出ConcurrentModificationException
? 它在迭代時修改Collection
,不使用Iterator.remove()
方法,這是刪除.
List<String> strings = new ArrayList<>(Arrays.asList("A", "B", "C"));
for (String string : strings)
if ("B".equals(string))
strings.remove("B");
System.out.println(strings);
如果我用LinkedList
替換ArrayList
,我會得到相同的結果。 但是,如果我將列表更改為("A", "B", "C", "D)
或只是("A", "B")
我會按預期得到異常。發生了什么?我正在使用jdk1.8.0_25
如果相關。
編輯
我找到了以下鏈接
http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4902078
相關部分是
天真的解決方案是向 AbstractList 中的 hasNext 添加協同修改檢查,但這會使協同修改檢查的成本增加一倍。 事實證明,只在最后一次迭代中進行測試就足夠了,這幾乎不會增加成本。 換句話說,hasNext 的當前實現:
public boolean hasNext() { return nextIndex() < size; }
被這個實現取代:
public boolean hasNext() { if (cursor != size()) return true; checkForComodification(); return false; }
由於 Sun 內部監管機構拒絕了此更改,因此不會進行此更改。 正式裁決表明,這一變化“已經證明了對現有代碼產生重大兼容性影響的潛力”。 (“兼容性影響”是修復有可能用 ConcurrentModificationException 替換沉默的不當行為。)
作為一般規則, ConcurrentModificationException
在檢測到修改時拋出,而不是導致。 如果您在修改后從未訪問過迭代器,則它不會拋出異常。 不幸的是,這個微小的細節使得ConcurrentModificationException
在檢測數據結構的誤用方面相當不可靠,因為它們僅在損壞完成后才會拋出。
這種情況不會拋出ConcurrentModificationException
因為在修改后不會在創建的迭代器上調用next()
。
For-each 循環實際上是迭代器,因此您的代碼實際上如下所示:
List<String> strings = new ArrayList<>(Arrays.asList("A", "B", "C"));
Iterator<String> iter = strings.iterator();
while(iter.hasNext()){
String string = iter.next();
if ("B".equals(string))
strings.remove("B");
}
System.out.println(strings);
考慮在您提供的列表中運行的代碼。 迭代看起來像:
hasNext()
返回 true,進入循環,-> iter 移動到索引 0,string = "A",未移除hasNext()
返回 true,繼續循環 -> iter 移動到索引 1,string = "B",已刪除。 strings
現在長度為 2。hasNext()
返回 false(iter 當前位於最后一個索引處,沒有更多索引可走),退出循環。 因此,當調用next()
檢測到已進行修改時會拋出ConcurrentModificationException
,因此這種情況可以勉強避免這種異常。
對於您的其他兩個結果,我們確實得到了例外。 對於"A", "B", "C", "D"
,在刪除 "B" 后,我們仍然在循環中,並且next()
檢測到ConcurrentModificationException
,而對於"A", "B"
我想它是某種 ArrayIndexOutOfBounds 被捕獲並作為ConcurrentModificationException
重新拋出
ArrayList 的迭代器中的hasNext
就是
public boolean hasNext() {
return cursor != size;
}
在remove
調用之后,迭代器在索引 2 處,列表的大小為 2,因此它報告迭代完成。 沒有並發修改檢查。 使用 ("A", "B", "C", "D) 或 ("A", "B") 時,迭代器不在列表的新末尾,因此調用next
並引發異常.
ConcurrentModificationException
只是一個調試輔助工具。 你不能依賴他們。
@Tavian Barnes 是完全正確的。 如果所討論的並發修改未同步,則不能保證拋出此異常。 引用java.util.ConcurrentModification
規范:
請注意,不能保證快速失敗行為,因為一般來說,在存在非同步並發修改的情況下不可能做出任何硬保證。 快速失敗操作會盡最大努力拋出 ConcurrentModificationException。 因此,編寫一個依賴此異常來確保其正確性的程序是錯誤的: ConcurrentModificationException 應該僅用於檢測錯誤。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.