什麼是fail-fast機制
如果我們對一個集合進行遍歷,而在集合遍歷的過程中對其進行修改(增加、刪除、修改)
或者 一個線程在對結合進行過遍歷,另外一個線程對其修改,則會報ConcurrentModificationException 異常。
舉例說明fail-fast 機制
- 場景: 單線程環境下,遍歷的時候,進行刪除
測試結果如下:
2、場景2: 多線程環境下,一個對集合進行遍歷元素。一個對集合刪除元素
測試結果:
3、場景3-對ArrayList 的subList進行操作
參考如下:
fail-fast 機制 為什麼會出現ConcurrentModificationException異常
我們先看下Iterator 類:
1、ArrayList 的add、set 等操作都會進行modCount++;
2、調用list.iterator時會new Itr();
創建Itr的過程,保存了 ArrayList中的modeCount,每次進行遍歷list的時候都會調用
checkForComodification方法:
上面檢查 expectedModCount和modCount是否相等,若不相等,則拋出異常
所以說在循環的時候對list進行remove, expectedModCount和modCoun不相等,所以會報異常(場景1),
3、為什麼多線程場景下也會出現ConcurrentModificationException 異常
上面的場景2,兩個線程初始化時,都持有list的modCount,當一個線程在遍歷,一個線程在刪除,
也會導致 expectedModCount和modCoun不相等。
怎麼解決fail-fast 出現的異常
1、在單線程環境下,我們可以不調用集合的remove方法,可以調用 Iterator的remove方法。
為什麼可以調用 Iterator 的remove方法呢?
從上面源碼可以看出,每次remove並不會修改modCount值,因為 每次remove只remove遍歷過的元素,所以不會發生fail-fast。
2、在多線程環境下,使用concurrent下的併發類
比如: 我們使用CopyOnArrayList 來代替 ArrayList,用ConcurrentHashMap 代理 HashMap
CopyOnArrayList參考下面文章。
使用 CopyOnArrayList 需要注意一下事項:
- 初始化 設置合理的初始如果能量,因為擴容的代價非常大
- 使用批量的添加(addAll)或者批量刪除(removeAll)操作,這個可以避免每次添加都會元素
- CopyOnArrayList只能保證最終一致性,不能保證每次讀取都是最新數據
總結
1、java.util 下的所有集合類都是fail-fast
2、concurrent 下的所有集合類都是fail-safe
3、在多線程環境下,儘量使用 concurrent 下的集合類
閱讀更多 碼農的一天 的文章