fail-fast和fail-safe

什麼是fail-fast和fail-safe?他們是迭代器訪問容器的兩種機制。

一:快速失敗(fail—fast)

在用迭代器遍歷一個集合對象時,如果遍歷過程中對集合對象的內容進行了修改(增加、刪除、修改),則會拋出Concurrent Modification Exception。

原理:迭代器在遍歷時直接訪問集合中的內容,並且在遍歷過程中使用一個 modCount 變量(modCount表示集合的修改次數,這其中包括了調用集合本身的add方法等修改方法時進行的修改和調用集合迭代器的修改方法進行的修改。而expectedModCount則是表示迭代器對集合進行修改的次數)。集合在被遍歷期間如果內容發生變化,就會改變modCount的值。每當迭代器使用hashNext()/next()遍歷下一個元素之前,都會檢測modCount變量是否為expectedmodCount值,是的話就返回遍歷;否則拋出異常,終止遍歷。

注意:這裡異常的拋出條件是檢測到 modCount!=expectedmodCount 這個條件。如果集合發生變化時修改modCount值剛好又設置為了expectedmodCount值,則異常不會拋出。因此,不能依賴於這個異常是否拋出而進行併發操作的編程,這個異常只建議用於檢測併發修改的bug。

場景:java.util包下的集合類都是快速失敗的,不能在多線程下發生併發修改(迭代過程中被修改)。

二:安全失敗(fail—safe)

採用安全失敗機制的集合容器,在遍歷時不是直接在集合內容上訪問的,而是先複製原有集合內容,在拷貝的集合上進行遍歷。

原理:由於迭代時是對原集合的拷貝進行遍歷,所以在遍歷過程中對原集合所作的修改並不能被迭代器檢測到,所以不會觸發Concurrent Modification Exception。

缺點:基於拷貝內容的優點是避免了Concurrent Modification Exception,但同樣地,迭代器並不能訪問到修改後的內容,即:迭代器遍歷的是開始遍歷那一刻拿到的集合拷貝,在遍歷期間原集合發生的修改迭代器是不知道的。

場景:java.util.concurrent包下的容器都是安全失敗,可以在多線程下併發使用,併發修改。


下面以代碼來實例演示:

public static void main(String[] args) {

List<string> strList = new ArrayList<string>();/<string>/<string>

//strList = new CopyOnWriteArrayList<string>();/<string>

for (int i = 0; i < 10; i++) {

strList.add(String.valueOf(i));

}

for (String str : strList) {

if ("2".equals(str)) {

strList.remove(str);

}

}

//Iterator<string> it = strList.iterator();/<string>

//while(it.hasNext()) {

//String str = it.next();

//if("2".equals(str)) {

//it.remove();

//}

//}

}

如上代碼所示:

1、當使用java.util.ArrayList時並且在foreach循環中使用容器的remove方法刪除時,會報異常ConcurrentModificationException

2、同樣情況下,使用java.util.concurrent.CopyOnWriteArrayList時,不報異常。

3、當使用java.util.ArrayList時並且在foreach循環中使用迭代器的remove方法刪除時,不會報異常。


另外,foreach本質上是語法糖,其實際實現是Iterator,如原代碼:

for(String str:strList) {

//...

}

相當於:

Iterator<string> it = strList.iterator();/<string>

while(it.hasNext()){

String str = it.next();

//...

}



分享到:


相關文章: