1. 程式人生 > >java中fail-fast 和 fail-safe的區別

java中fail-fast 和 fail-safe的區別

原文地址:http://javahungry.blogspot.com/2014/04/fail-fast-iterator-vs-fail-safe-iterator-difference-with-example-in-java.html

在我們詳細討論這兩種機制的區別之前,首先得先了解併發修改。

1.什麼是同步修改?

當一個或多個執行緒正在遍歷一個集合Collection,此時另一個執行緒修改了這個集合的內容(新增,刪除或者修改)。這就是併發修改

2.什麼是 fail-fast 機制?

fail-fast機制在遍歷一個集合時,當集合結構被修改,會丟擲Concurrent Modification Exception。

fail-fast會在以下兩種情況下丟擲ConcurrentModificationException

(1)單執行緒環境

集合被建立後,在遍歷它的過程中修改了結構。

注意 remove()方法會讓expectModcount和modcount 相等,所以是不會丟擲這個異常。

(2)多執行緒環境

當一個執行緒在遍歷這個集合,而另一個執行緒對這個集合的結構進行了修改。

注意,迭代器的快速失敗行為無法得到保證,因為一般來說,不可能對是否出現不同步併發修改做出任何硬性保證。快速失敗迭代器會盡最大努力丟擲 ConcurrentModificationException。因此,為提高這類迭代器的正確性而編寫一個依賴於此異常的程式是錯誤的做法:迭代器的快速失敗行為應該僅用於檢測 bug。


3. fail-fast機制是如何檢測的?

迭代器在遍歷過程中是直接訪問內部資料的,因此內部的資料在遍歷的過程中無法被修改。為了保證不被修改,迭代器內部維護了一個標記 “mode” ,當集合結構改變(新增刪除或者修改),標記"mode"會被修改,而迭代器每次的hasNext()和next()方法都會檢查該"mode"是否被改變,當檢測到被修改時,丟擲Concurrent Modification Exception

。下面看看ArrayList迭代器部分的原始碼

private class Itr implements Iterator<E> {
        int cursor;
        int lastRet = -1;
        int expectedModCount = ArrayList.this.modCount;

        public boolean hasNext() {
            return (this.cursor != ArrayList.this.size);
        }

        public E next() {
            checkForComodification();
            /** 省略此處程式碼 */
        }

        public void remove() {
            if (this.lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();
            /** 省略此處程式碼 */
        }

        final void checkForComodification() {
            if (ArrayList.this.modCount == this.expectedModCount)
                return;
            throw new ConcurrentModificationException();
        }
    }

可以看到它的標記“mode”為 expectedModeCount

4. fail-safe機制

fail-safe任何對集合結構的修改都會在一個複製的集合上進行修改,因此不會丟擲ConcurrentModificationException

fail-safe機制有兩個問題

(1)需要複製集合,產生大量的無效物件,開銷大

(2)無法保證讀取的資料是目前原始資料結構中的資料。

5 fail-fast 和 fail-safe的例子

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class FailFastExample
{
    
    
    public static void main(String[] args)
    {
        Map<String,String> premiumPhone = new HashMap<String,String>();
        premiumPhone.put("Apple", "iPhone");
        premiumPhone.put("HTC", "HTC one");
        premiumPhone.put("Samsung","S5");
        
        Iterator iterator = premiumPhone.keySet().iterator();
        
        while (iterator.hasNext())
        {
            System.out.println(premiumPhone.get(iterator.next()));
            premiumPhone.put("Sony", "Xperia Z");
        }
        
    }
    
}

輸出
iPhone 
Exception in thread "main" java.util.ConcurrentModificationException
        at java.util.HashMap$HashIterator.nextEntry(Unknown Source)
        at java.util.HashMap$KeyIterator.next(Unknown Source)
        at FailFastExample.main(FailFastExample.java:20)

import java.util.concurrent.ConcurrentHashMap;
import java.util.Iterator;


public class FailSafeExample
{
    
    
    public static void main(String[] args)
    {
        ConcurrentHashMap<String,String> premiumPhone = 
                               new ConcurrentHashMap<String,String>();
        premiumPhone.put("Apple", "iPhone");
        premiumPhone.put("HTC", "HTC one");
        premiumPhone.put("Samsung","S5");
        
        Iterator iterator = premiumPhone.keySet().iterator();
        
        while (iterator.hasNext())
        {
            System.out.println(premiumPhone.get(iterator.next()));
            premiumPhone.put("Sony", "Xperia Z");
        }
        
    }
    
}

輸出
S5
HTC one
iPhone

6. fail-fast和 fail-safe 的區別

Fail Fast Iterator Fail Safe Iterator
Throw ConcurrentModification Exception Yes No
Clone object No Yes
Memory Overhead No Yes
Examples HashMap,Vector,ArrayList,HashSet
CopyOnWriteArrayList,
ConcurrentHashMap