前面回顧: HashMapd的存取原理你知道多少
今天,我們來談談fail-fast與fail-safe是什麼以及工作機制
複製程式碼
fail-fast是什麼?
fail-fast的字面意思是“快速失敗”。當我們在遍歷集合元素的時候,經常會使用迭代器,但在迭代器遍歷元素的過程中,如果集合的結構被改變的話,就會丟擲異常,防止繼續遍歷。這就是所謂的快速失敗機制。
下面我們來看看官方文件在HashMap這個集合中,它是怎麼解釋fail-fast的(如下圖):
意思就是說,當Iterator這個迭代器被建立後,除了迭代器本身的方法(remove)可以改變集合的結構外,其他的因素如若改變了集合的結構,都被丟擲ConcurrentModificationException異常。
請在繼續看官方的描述:
意思就是說:迭代器的快速失敗行為是不一定能夠得到保證的,一般來說,存在非同步的併發修改時,不可能做出任何堅決的保證的。但是快速失敗迭代器會做出最大的努力來丟擲ConcurrentModificationException。因此,編寫依賴於此異常的程式的做法是不正確的。正確的做法應該是:迭代器的快速失敗行為應該僅用於檢測程式中的bug.
稍微總結下:fail-fast,即快速失敗機制,它是java集合中的一種錯誤檢測機制,當多個執行緒(當個執行緒也是可以滴),在結構上對集合進行改變時,就有可能會產生fail-fast機制。
這裡,我解釋下什麼是結構上的改變。 例如集合上的插入和刪除就是結構上的改變,但是,如果是對集合中某個元素進行修改的話,並不是結構上的改變哦。
下面,我們來演示下在單執行緒的環境下,fail-fast丟擲異常的例項:
for(int i = 10; i < 100; i++){
map.put(i, i);
}
List<Integer> list = new ArrayList<>();
for(int i = 0; i < 20; i++){
list.add(i);
}
Iterator<Integer> it = list.iterator();
int temp = 0;
while(it.hasNext()){
if(temp == 3){
temp++;
list.remove(3);
}else{
temp++;
System.out.println(it.next());
}
}
}
複製程式碼
列印結果:
**結果分析:**因為當temp==3的時候,執行list.remove()方法,集合的結構被改變了,所以再次遍歷迭代器的時候,就會丟擲異常。
fail-fast的工作原理
我們首先先來看下原始碼:
**分析:**從原始碼我們可以發現,迭代器在執行next()等方法的時候,都會呼叫checkForComodification()這個方法,檢視modCount==expectedModCount?如果相等則丟擲異常。
expectedModcount:這個值在物件被建立的時候就被賦予了一個固定的值modCount。也就是說這個值是不變的。也就是說,如果在迭代器遍歷元素的時候,如果modCount這個值發生了改變,那麼再次遍歷時就會丟擲異常。
什麼時候modCount會發生改變呢?
這次就不帶大家看原始碼了。其實當我們對集合的元素的個數做出改變的時候,modCount的值就會被改變,如果刪除,插入。但修改則不會。
fail-fast的一些處理方法
如果我們不希望在迭代器遍歷的時候因為併發等原因,導致集合的結構被改變,進而可能丟擲異常的話,我們可以在涉及到會影響到modCount值改變的地方,加上同步鎖(synchronized),或者直接使用 Collections.synchronizedList來解決。
fail-fast就先講到這裡,下面簡單講講fail-safe與fail-fast的區別
當我們對集合結構上做出改變的時候,fail-fast機制就會丟擲異常。但是,對於採用fail-safe機制來說,就不會丟擲異常(大家估計看到safe兩個字就知道了)。
這是因為,當集合的結構被改變的時候,fail-safe機制會在複製原集合的一份資料出來,然後在複製的那份資料遍歷。
因此,雖然fail-safe不會丟擲異常,但存在以下缺點:
-
複製時需要額外的空間和時間上的開銷。
-
不能保證遍歷的是最新內容。
如果文章有錯的地方歡迎指正。如果習慣在微信看技術文章,想要獲取更多其他資源的同學,可以關注微信公眾號 :苦逼的碼農
希望看完讓你有所收穫,便是我最大的回報!