java設計模式之--單例模式

單例模式是GoF設計模式其中的一種而且屬於創造的設計模式目錄。java

定義上,這彷佛是一種很是簡單的設計模式,但從實現的角度來講涉及到很是多的方面。
單例模式的實如今開發者中一直是個頗有爭議的話題。數據庫

這裏咱們將學習單例模式原則,不一樣的單例實現方式和最佳實踐。設計模式

 

單例模式

單例模式限制類的實例和確保java類在java虛擬機中只有一個實例的存在。緩存

單例類必須提供一個全局的訪問來獲取類的實例。安全

單例模式用來日誌,驅動對象,緩存和線程池。多線程

單例設計模式也用在其餘設計模式,例如抽象工廠,建造者,原型,門面等設計模式。性能

單例模式還用在覈心java中,例如java.lang.Runtime, java.awt.Desktop學習

 

java單例模式

爲了實現Singleton模式,咱們有不一樣的方法,但它們都有如下共同的概念。spa

  • 私有構造方法限制從其餘類初始化類的實例。
  • 私有靜態變量與該類的實例相同。
  • 公有靜態方法返回類的實例,這是提供給外部訪問的全局訪問點來獲取單例類的實例。在如下的章節,咱們將學習單例模式的不一樣實現方法。

實現的類型線程

  • 餓漢模式
  • 靜態初始化
  • 懶加載
  • 線程安全的單例
  • Bill Pugh單例實現(比爾·普格單例實現)
  • 枚舉單例

 

餓漢模式

餓漢模式就是當類加載時就建立該類的實例,這是建立單例類最容易的方法可是有個一弊端是建立了該實例可是客戶端程序可能不使用這個實例。

如下是靜態初始化單例類的實現。餓漢式天生就是線程安全的。

public class EagerInitializedSingleton {

    private static EagerInitializedSingleton instance  = new EagerInitializedSingleton();

    private EagerInitializedSingleton(){

    }

    public static EagerInitializedSingleton getInstance(){
        return instance;
    }
}

 

在大多數的情景,單例類的建立是爲了如文件系統,數據庫鏈接等資源的管理。咱們應該避免過早的建立類的實例化,除非直到客戶端調用getInstance()方法。
這種方法也沒有提供異常處理的任何選項。

 

靜態初始化塊

靜塊初始化實現相似於餓漢模式初始化,但類的實例在靜態代碼塊中建立並對異常進行處理。

public class StaticBlockSingleton {
    private static StaticBlockSingleton instance ;

    static {
        try{
            instance = new StaticBlockSingleton();
        }catch (Exception e){
            throw new RuntimeException("靜態代碼塊中實例化失敗");
        }

    }

    public static StaticBlockSingleton getInstance(){
        return instance;
    }

}

 

餓漢模式和靜態初始化兩種實現都是在實例在被使用以前就已經建立了,這不是最佳實踐。
如下章節中,咱們將會學習如何建立支持懶加載的單例類。

 

懶加載

相比以前的兩種方法,懶加載就是當須要的時候再來建立該類的實例,而不是一開始就把實例建立好了。

public class LazyInitializedSingleton {
    private static LazyInitializedSingleton instance = null;

    private LazyInitializedSingleton(){

    }

    public static LazyInitializedSingleton getInstance(){
        if(instance == null){
            instance = new LazyInitializedSingleton();
        }
        return  instance;
    }

}

 

懶加載的實如今單線程環境中能夠正常使用,可是在多線程環境中會引起一些問題。這將會破壞單例模式和線程會獲取不一樣的單例對象(不能保證線程安全) 。下文中,將會經過不一樣方法來實現線程安全的單例類。

 

線程安全的單例

public class ThreadSafeSingleton {
    private static ThreadSafeSingleton instance = null;

    private ThreadSafeSingleton(){

    }

    //同步關鍵字synchronized
    public static synchronized ThreadSafeSingleton getInstance(){
        if(instance == null){
            instance = new ThreadSafeSingleton();
        }
        return  instance;
    }

}

 

上述實現能正常運行,並提供了線程安全,但它下降了程序的性能,由於synchronized關鍵字修飾的是getInstance()整個方法。
但咱們須要它僅用於誰可能建立單獨的實例的第一個線程數(閱讀:Java同步)。爲了不這種每一次的額外開銷,使用雙檢查鎖定原理。在這種方法中,synchronized塊使用if條件裏面加上一個額外的檢查,以確保只建立一個單獨的類的實例。

public static ThreadSafeSingleton getInstanceUsingDoubleLocking(){
    //synchronized同步塊,控制更細的粒度
    if(instance == null){//此層的控制容許第一個線程進入訪問,避免以上狀況(同步方法)每次的等待開銷。
        synchronized (ThreadSafeSingleton.class) {
            if(instance == null){//此處校驗單例是否已經被建立,確實只有一個實例的存在。
                instance = new ThreadSafeSingleton();
            }
        }
    }
    return instance;
}

 

 

Bill Pugh單例

比爾普格想出了一個不一樣的方法來建立一個使用靜態內部輔助類的Singleton類。

public class BillPughSingleton {

    private static class SingleTonHelpClass{
        private static  final BillPughSingleton INSTANCE = new BillPughSingleton();
    }

    public static BillPughSingleton getInstance(){
        return SingleTonHelpClass.INSTANCE;
    }

}

 

當單例類被加載,SingletonHelper內部類沒有加載到內存中,只有當調用getInstance()方法時,該類被載入並建立Singleton類的實例。這種單例類由於它不要求使用同步的方法,容易理解和實現。

 

枚舉單例

這種方法簡單,便捷。

public enum  EnumSingleton {
    INSTANCE;

    public static void doSomething(){
        //do something
    }
}

轉載請註明出處,原文連接https://zhuanlan.zhihu.com/p/20831029

相關文章
相關標籤/搜索