單例模式的五種寫法

callmedashen發表於2018-10-18

寫之前的話

這是從簡書移植來的。
單例模式的五種寫法

經典版單例模式

public class Singleton {
  
    private static Singleton uniqueInstance;//利用一個靜態常量來記錄singleton類的唯一例項。

    private Singleton() {
    }

    public static  Singleton getInstance() {
        if (uniqueInstance == null) {//注意,只有第一次才徹底執行這裡的程式碼
            uniqueInstance = new Singleton();//延遲例項化 (lazy instantiaze)
        }
        return uniqueInstance;//如果uniqueInstance不是null,就表示之前建立過物件,
        // 我們就直接跳到return語句
    }
}

這個版本存在一些問題,當不止一個執行緒的時候,以兩個執行緒為例,兩個執行緒同時執行到 if (uniqueInstance == null) {}時,因為兩個執行緒裡類此時都沒有例項化,因此這裡還會new兩個例項,所以經典模式只適合同步的情況下。

同步鎖單例模式

public class Singleton {
  
    private static Singleton uniqueInstance;//利用一個靜態常量來記錄singleton類的唯一例項。

    private Singleton() {
    }

    public static synchronized Singleton getInstance() {
        if (uniqueInstance == null) {//注意,只有第一次才徹底執行這裡的程式碼
            uniqueInstance = new Singleton();//延遲例項化 (lazy instantiaze)
        }
        return uniqueInstance;//如果uniqueInstance不是null,就表示之前建立過物件,
        // 我們就直接跳到return語句
    }
}

通過新增synchronized關鍵字到getInstance方法中,我們強迫每個進入執行緒中進入該方法的前提是別的執行緒離開該方法,不會有兩個執行緒同時進入該方法。這種單例模式是執行緒安全的,不過synchronized會影響程式的效能,只有第一次執行該方法例項化的時候才需要同步,一旦設定好uniqueInstance就不再需要同步這個方法了。因此之後每次呼叫這個方法,synchronized都是一種累贅。

急切式單例模式(直接在靜態變數建立單例)

public class Singleton {
  
    private static Singleton uniqueInstance = new Singleton();//利用一個靜態常量來記錄singleton類的唯一例項。

    private Singleton() {
    }

    public static Singleton getInstance() {
        return uniqueInstance;//已經有單例了,直接使用就好了
    }
}

利用這種寫法,JVM在家在這個類時馬上建立此唯一的單例。JVM保證任何執行緒在訪問uniqueInstance靜態變數時,一定先建立次例項。

雙重同步鎖單例模式

public class Singleton {
    //volatile 關鍵詞確保:當uinqueInstance變數被初始化成Singleton例項時,
    // 多個執行緒正確的處理uniqueInstance變數
    private volatile static Singleton uniqueInstance;//利用一個靜態常量來記錄singleton類的唯一例項。

    private Singleton() {

    }
//防止多執行緒出錯
    public static  Singleton getInstance() {//檢查例項,如果不存在,進入同步區塊。
        if (uniqueInstance == null) {//注意,只有第一次才徹底執行這裡的程式碼
            synchronized (Singleton.class) {
                //進入區塊後在檢查一次,如果仍是null,才建立例項。
                if (uniqueInstance == null) {
                    uniqueInstance = new Singleton();//延遲例項化 (lazy instantiaze)
                }
            }
        }
        return uniqueInstance;//如果uniqueInstance不是null,就表示之前建立過物件,
        // 我們就直接跳到return語句
    }
}

想比同步鎖單例模式,這種做法可以大大提高效能。
volatile 關鍵詞:當uinqueInstance變數被初始化成Singleton例項時,多個執行緒正確的處理uniqueInstance變數。

用列舉實現單例模式

public enum Singleton {
    instance;
    private xxxxxx instance;
    Singleton () {
        instance = new xxxxxx ();
    }
    public xxxxxx getInstance() {
        return instance;
    }
}

上面的類xxxxxx 是我們要應用單例模式的資源,具體可以表現為網路連線,資料庫連線,執行緒池等等。獲取資源的方式很簡單,只要使用Singleton.instance.getInstance() 即可獲得所要例項。在列舉中我們明確了構造方法限制為私有,在我們訪問列舉例項時會執行構造方法,同時每個列舉例項都是static final型別的,也就表明只能被例項化一次。在呼叫構造方法時,我們的單例被例項化。 也就是說,因為enum中的例項被保證只會被例項化一次,所以我們的instance也被保證例項化一次。

結束

以上就是單例模式的幾種用法,每種方法都有各自優缺點,單元素的列舉型別已經成為實現Singleton的最佳方法。

相關文章