fail-fast和fail-safe的介紹和區別

fail-fast和fail-safe
前言
前段時間公司招的實習生在使用迭代器遍歷的時候,對集合內容進行了修改,從而拋出ConcurrentModificationException. 而後給他講解之餘也整理了這一篇文章.java

fail-fast ( 快速失敗 )
在使用迭代器遍歷一個集合對象時,好比加強for,若是遍歷過程當中對集合對象的內容進行了修改(增刪改),會拋出 ConcurrentModificationException 異常.編程

查看ArrayList源代碼,在next方法執行的時候,會執行checkForComodification()方法安全

@SuppressWarnings("unchecked")  
public E next() {  
    checkForComodification();  
    int i = cursor;  
    if (i >= size)  
        throw new NoSuchElementException();  
    Object[] elementData = ArrayList.this.elementData;  
    if (i >= elementData.length)  
        throw new ConcurrentModificationException();  
    cursor = i + 1;  
    return (E) elementData[lastRet = i];  
}  

//...............省略.............

final void checkForComodification() {  
    if (modCount != expectedModCount)  
        throw new ConcurrentModificationException();  
}  


原理:多線程

迭代器在遍歷時直接訪問集合中的內容,而且在遍歷過程當中使用一個modCount變量,
集合中在被遍歷期間若是內容發生變化,就會改變modCount的值,
每當迭代器使用 hashNext()/next()遍歷下一個元素以前,都會檢測modCount變量和expectedmodCount值是否相等,
若是相等就返回遍歷,不然拋出異常,終止遍歷.
舉例併發

//會拋出ConcurrentModificationException異常
for(Person person : Persons){
    if(person.getId()==2)
        student.remove(person);
}

注意
這裏異常的拋出條件時檢測到modCount = expectedmodCount 這個條件.this

若是集合發生變化時修改modCount值, 恰好有設置爲了expectedmodCount值, 則異常不會拋出.(好比刪除了數據,再添加一條數據)線程

//不會拋出ConcurrentModificationException異常
for(Person person : Persons){
    if(person.getId()==2){
        Persons.remove(person);
        Persons.add(new Person());
    }
}

因此不能依賴於這個異常是否拋出而進行併發操做的編程, 這個異常只建議檢測併發修改的bug.code

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

fail-safe ( 安全失敗 )
採用安全失敗機制的集合容器,在遍歷時不是直接在集合內容上訪問的,而是先copy原有集合內容,在拷貝的集合上進行遍歷.element

原理:

因爲迭代時是對原集合的拷貝的值進行遍歷,因此在遍歷過程當中對原集合所做的修改並不能被迭代器檢測到,因此不會出發ConcurrentModificationException
缺點:

基於拷貝內容的優勢是避免了ConcurrentModificationException,但一樣地, 迭代器並不能訪問到修改後的內容 (簡單來講就是, 迭代器遍歷的是開始遍歷那一刻拿到的集合拷貝,在遍歷期間原集合發生的修改迭代器是不知道的) 使用場景: java.util.concurrent包下的容器都是安全失敗的,能夠在多線程下併發使用,併發修改.

相關文章
相關標籤/搜索