1. 程式人生 > >java 單例模式的幾種寫法

java 單例模式的幾種寫法

一、懶漢式

public class Singleton{
    private static Singleton instance = null;
    private Singleton(){}
    public static Singleton newInstance(){
        if(null == instance){
            instance = new Singleton();
        }
        return instance;
    }
}

  懶漢模式中單例是在需要的時候才去建立的,如果單例已經建立,再次呼叫獲取介面將不會重新建立新的物件,而是直接返回之前建立的物件。如果某個單例使用的次數少,並且建立單例消耗的資源較多,那麼就需要實現單例的按需建立,這個時候使用懶漢模式就是一個不錯的選擇。但是這裡的懶漢模式並沒有考慮執行緒安全問題,在多個執行緒可能會併發呼叫它的getInstance()方法,導致建立多個例項

二、餓漢式

public class Singleton{
    private static Singleton instance = new Singleton();
    private Singleton(){}
    public static Singleton newInstance(){
        return instance;
    }
}

  從程式碼中我們看到,類的建構函式定義為private的,保證其他類不能例項化此類,然後提供了一個靜態例項並返回給呼叫者。餓漢模式是最簡單的一種實現方式,餓漢模式在類載入的時候就對例項進行建立,例項在整個程式週期都存在。它的好處是隻在類載入的時候建立一次例項,不會存在多個執行緒建立多個例項的情況,避免了多執行緒同步的問題。它的缺點也很明顯,即使這個單例沒有用到也會被建立,而且在類載入之後就被建立,記憶體就被浪費了。

三、懶漢加鎖式

public class Singleton{
    private static Singleton instance = null;
    private Singleton(){}
    public static synchronized Singleton newInstance(){
        if(null == instance){
            instance = new Singleton();
        }
        return instance;
    }
}

  加鎖的懶漢模式看起來即解決了執行緒併發問題,又實現了延遲載入,然而它存在著效能問題,依然不夠完美。synchronized修飾的同步方法比一般方法要慢很多,如果多次呼叫getInstance(),累積的效能損耗就比較大了。

四、懶漢雙重校驗鎖

 

public class Singleton {
    private static volatile Singleton instance = null;
    private Singleton(){}
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

 

1.為了防止new Singleton被執行多次,因此在new操作之前加上Synchronized 同步鎖,鎖住整個類(注意,這裡不能使用物件鎖)。

2.進入Synchronized 臨界區以後,還要再做一次判空。因為當兩個執行緒同時訪問的時候,執行緒A構建完物件,執行緒B也已經通過了最初的判空驗證,不做第二次判空的話,執行緒B還是會再次構建instance物件。

3.JVM編譯器的指令重排會導致併發問題,所以加入volatile關鍵字防止指令重排

 

五、靜態內部類

public class Singleton{
    private static class SingletonHolder{ public static Singleton instance = new Singleton(); } private Singleton(){} public static Singleton newInstance(){ return SingletonHolder.instance; } }

  這種方式同樣利用了類載入機制來保證只建立一個instance例項。它與餓漢模式一樣,也是利用了類載入機制,因此不存在多執行緒併發的問題。不一樣的是,它是在內部類裡面去建立物件例項。這樣的話,只要應用中不使用內部類,JVM就不會去載入這個單例類,也就不會建立單例物件,從而實現懶漢式的延遲載入。也就是說這種方式可以同時保證延遲載入和執行緒安全。

六、列舉

public enum SingletonEnum {
    INSTANCE;
}

  使用列舉實現的單例模式,不但可以防止利用反射強行構建單例物件,而且可以在列舉類物件被反序列化的時候,保證反序列的返回結果是同一物件。但是列舉模式也是餓漢式,和普通餓漢式存在同樣的問題