迭代器的快速失敗行為無法得到保證,因為一般來說,不可能對是否出現不同步並發修改做出任何硬性保證。快速失敗迭代器會盡最大努力拋出 ConcurrentModificationException,為提高這類迭代器的正確性而編寫一個依賴於此異常的程序是錯誤的做法:迭代器的快速失敗行為應該僅用於檢測 bug。
它是Java集合的一種錯誤檢測機制。當多個線程對集合進行結構上的改變的操作時,有可能會產生fail-fast機制。記住是有可能,而不是一定。
ConcurrentModificationException不會始終指出對象已經由不同線程並發修改,如果單線程違反了規則,同樣也有可能會拋出該異常。
迭代器在調用next()、remove()方法時都是調用checkForComodification()方法,該方法主要就是檢測modCount == expectedModCount ? 若不等則拋出ConcurrentModificationException 異常,從而產生fail-fast機制。
fail-fast解決方案:(對於ArrayList)
方案一:在遍歷過程中所有涉及到改變modCount值得地方全部加上synchronized或者直接使用Collections.synchronizedList,這樣就可以解決。但是不推薦,因為增刪造成的同步鎖可能會阻塞遍歷操作。
方案二:使用CopyOnWriteArrayList來替換ArrayList。
CopyOnWriteArrayList所有可變操作(add、set 等等)都是通過對底層數組進行一次新的復制來實現的。
該類產生的開銷比較大,但是在兩種情況下,它非常適合使用。1:在不能或不想進行同步遍歷,但又需要從並發線程中排除沖突時。2:當遍歷操作的數量大大超過可變操作的數量時。
CopyOnWriterArrayList根本就不會產生ConcurrentModificationException異常,也就是它使用迭代器完全不會產生fail-fast機制。
public boolean add(E paramE) { ReentrantLock localReentrantLock = this.lock; localReentrantLock.lock(); try { Object[] arrayOfObject1 = getArray(); int i = arrayOfObject1.length; Object[] arrayOfObject2 = Arrays.copyOf(arrayOfObject1, i + 1); arrayOfObject2[i] = paramE; setArray(arrayOfObject2); int j = 1; return j; } finally { localReentrantLock.unlock(); } } final void setArray(Object[] paramArrayOfObject) { this.array = paramArrayOfObject; }
以上紅色的三句代碼使得CopyOnWriterArrayList不會拋ConcurrentModificationException異常。他們所展現的魅力就在於copy原來的array,再在copy數組上進行add操作,這樣做就完全不會影響COWIterator中的array了。
任何對array在結構上有所改變的操作(add、remove、clear等),CopyOnWriterArrayList都會copy現有的數據,再在copy的數據上修改,這樣就不會影響COWIterator中的數據了,修改完成之后改變原有數據的引用即可。同時這樣造成的代價就是產生大量的對象,同時數組的copy也是相當有損耗的.