1. 程式人生 > >【乾貨】無可挑剔的幾種Singleton寫法

【乾貨】無可挑剔的幾種Singleton寫法

餓漢式

public class Singleton1 {
    /**
     * 宣告final static,保證全域性可訪問且不可變。<br>
     * 宣告transient,保證不會被序列化,當然也就不會反序列化生成物件了。
     */
    private final static transient Singleton1 instance = new Singleton1();

    private Singleton1() {
        /**
         * 防止通過反射生成物件
         */
        if (instance != null
) { throw new RuntimeException(""); } } public static Singleton1 getInstance() { return instance; } }

不管你用不用,只要類初始化了,物件就在那裡。

懶漢式

public class Singleton2 {
    /**
     * volatile:保證物件生成後,其它執行緒立即可見,避免多餘的加鎖流程。<br>
     * transient:保證不會被序列化,當然也就不會反序列化生成物件了。
     */
private static volatile transient Singleton2 instance = null; private Singleton2() { /** * 防止通過反射生成物件 */ if (instance != null) { throw new RuntimeException(""); } } public static Singleton2 getInstance() { //用到了雙重檢測和加鎖,目的是最大化的提高程式的執行速度
if (instance == null) {// 檢測物件是否已經生成,此時不加鎖。 synchronized (Singleton2.class) {//加鎖 if (instance == null)// 加鎖情況下檢測物件是否已經生成,防止執行緒在等待鎖時其它執行緒已經生成了物件 instance = new Singleton2(); } } return instance; } }

只有在使用時才會生成物件,但會導致執行緒加鎖,執行緒競爭嚴重時不建議使用該方式。

靜態內部類

public class Singleton3 {
    private static class SingletonHolder {
        private final transient static Singleton3 instance = new Singleton3();
    }

    private Singleton3() {
        /**
         * 防止通過反射生成物件
         */
        if (SingletonHolder.instance != null) {
            throw new RuntimeException("");
        }
    }

    public static Singleton3 getInstance() {
        return SingletonHolder.instance;
    }
}

同時具有懶漢和餓漢兩個模式的優點:避免多執行緒競爭,而且只在使用時才例項化物件。

列舉式

public enum Singleton4 {
    INSTANCE;
    public void doSomething() {
        System.out.println("aha");
    }
}

public static void main(String[] args) {
        Singleton4.INSTANCE.doSomething();
}

程式碼安全無競爭,寫法簡潔到想哭。

反思

我覺得沒有必要關注太細的細節,比如防止反射,防止序列化;大部分情況下,我們在開發程式碼時都已經預知了單例的使用場景。
大神閆巨集在設計模式一書中提到一種單例模式:建構函式是public的。這樣我們可以在使用單例時呼叫getInstance方法,使用多例時直接new一個就好。因為這個類的使用場景是預先知道的。誰又能說這不是一個很好的單例模式?
不必在意茴香豆有幾種寫法,會寫茴香豆幾個字就好。