java中的快速失敗機制(fail-fast)安全失敗機制(fail-safe)


一:快速失敗(fail—fast)

        迭代器模式:就是提供一種方法對一個容器對象中的各個元素進行訪問,而又不暴露該對象容器的內部細節。

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

          原理:迭代器在遍歷時直接訪問集合中的內容,並且在遍歷過程中使用一個 modCount 變量。集合在被遍歷期間如果內容發生變化,就會改變modCount的值。每當迭代器使用hashNext()/next()遍歷下一個元素之前,都會檢測modCount變量是否為expectedmodCount值,是的話就返回遍歷;否則拋出異常,終止遍歷。               

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

 

private static ArrayList<String> list = new ArrayList<String>();
private
static void testFailFast() { for(int i=0;i<5;i++) { list.add(String.valueOf(i)); } Iterator iter=list.iterator(); String value=null; while(iter.hasNext()) { value = (String)iter.next(); System.out.print(value+", "); list.remove(value);//在用Iterator遍歷的過程中,如果在原集合對象上進行修改就會報ConcurrentModificationException錯誤 } printAll();
}

但是你直接在Iterator上操作就不會報錯

private static void testFailFast() {       
        for(int i=0;i<5;i++) {
            list.add(String.valueOf(i));
        }
        Iterator iter=list.iterator();
        String value=null;
        while(iter.hasNext()) {
          value = (String)iter.next();
          System.out.print(value+", ");
          iter.remove();
        }
        printAll();
}
集合被全部刪除,這是因為在Iterator的remove操作中更新了expectedModCount=modCount的值,而集合對象自己的remove方法中是沒有這個操作的

在多線程的情況下同單線程,主要就是在用Iterator遍歷的時候不要直接操作原集合對象。

         

二:安全失敗(fail—safe)

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

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

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

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

private static CopyOnWriteArrayList<String> safelist = new CopyOnWriteArrayList<String>();
private
static void testFailSafe() { for(int i=0;i<5;i++) { safelist.add(String.valueOf(i)); } String value=null; Iterator iter=safelist.iterator(); while(iter.hasNext()) { value = (String)iter.next(); System.out.print(value+", "); safelist.remove(value);//可以在原集合對象上操作,沒有報錯 } printAll(); }

 

 使用Foreach時對集合的結構進行修改會出現異常:

  上面我們說了實現了Iterable接口的類就可以通過Foreach遍歷,那是因為foreach要依賴於Iterable接口返回的Iterator對象,所以從本質上來講,Foreach其實就是在使用迭代器,在使用foreach遍歷時對集合的結構進行修改,和在使用Iterator遍歷時對集合結構進行修改本質上是一樣的。所以同樣的也會拋出異常,執行快速失敗機制。

 for循環與迭代器的對比:

         效率上各有各的優勢:

    ArrayList對隨機訪問比較快,而for循環中使用的get()方法,采用的即是隨機訪問的方法,因此在ArrayList里for循環快。

    LinkedList則是順序訪問比較快,Iterator中的next()方法采用的是順序訪問方法,因此在LinkedList里使用Iterator較快。

    主要還是要依據集合的數據結構不同的判斷。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM