【23種設計模式之一】單例設計模式(翻譯)

引言:java

    這一系列文章,翻譯自網絡上的文章,不過中間會夾雜着我的的理解,非原創,不過中文應該算是原創。
數據庫

    下面介紹,使用設計模式的一些好處:
設計模式

    一、設計模式是已經在工業生產中使用的,用於解決特定問題的標準方法,若是你在遇到相似問題的時候,能參考相應的設計,會節省你不少的時間。
api

    二、設計模式,每每能提升代碼的重用性,會減小你的開發時間。
緩存

    三、既然你們都知道設計模式,包括從你的類的命名上面,類的使用上面,你們都很清楚的知道相應類的具體功能,就像每一個人都約定好的那樣。
安全

 分類:
網絡

    java設計模式分爲3大類:
多線程

    一、對象建立
併發

    二、結構
app

    三、行爲

對象建立設計模式的定義

    一、在特定的問題場景中,建立設計模式會合理的根據特定的問題建立對象。由於對象的建立可能會很複雜,會增長沒必要要的複雜性,建立設計模式以不一樣的建立方法解決了這些複雜性。

對象建立設計模式包含的範圍:

    一、單例

    二、工廠

    三、抽象工廠

    四、建造者

    五、原型

    本節會講一下,對象建立中的單例設計模式。

    單例設計模式,大部分人第一印象感受很簡單,看完這篇博客,我想你就不會這麼認爲啦,不一樣的開發者對於以何種方式實現單例設計模式是有爭議的,本文儘量的把目前實現單例的方法歸納出來,並給出最佳實戰的建議。

單例模式的定義:

    單例模式是一種建立對象實例的設計模式,在建立對象的過程當中,他會保證,在一個java虛擬機裏面只有一個對象實例,同時單例設計模式,必須提供一個給外部訪問該惟一對象的入口。

單例模式的應用場景:

    日誌、驅動對象、緩存、線程池等等,固然設計模式不是單一存在的,其它設計模式中也會使用單例設計模式來完成一些事情,好比:抽象工廠,建造者,原型,門面等。同時在java核心包裏面也有其身影,好比java.lang.runtime.

單例模式的實現方法的共性:

    一、提供私有的構造函數,防止外部類直接初始化該對象

    二、提供私有的靜態變量,固然要是該類的對象實例,惟一的對象實例。

    三、public的靜態方法,這就是上面說的,提供給外部類獲取該類對象的惟一入口。

接下來,咱們會以不一樣的方法,來實現單例設計模式。

【A】飢餓實現

    飢餓實現就是,等不急,當類被classloader加載的時候就初始化了該對象,這種實現有個弊端就是,當client不使用該類實例就浪費啦,不過我我的以爲,這種狀況不存在,你不用他寫他作什麼呢?代碼測試要有覆蓋率的,測試每天在你後面催着喊你把他刪掉。

public class EagerInitializedSingleton 
{
     private static final EagerInitializedSingleton instance = new
     EagerInitializedSingleton();
    //private constructor to avoid client applications to use constructor
     private EagerInitializedSingleton(){}
     public static EagerInitializedSingleton getInstance()
     {
        return instance;
     }
}

    若是你的單例對象在建立的時候不使用過多的資源,這種方法是可行的。可是大部分的時候,單例對象建立的時候會加載不少的資源文件,好比File system,數據庫鏈接等等。咱們應該避免在客戶端調用getInstance方法以前建立這個對象,可是這種方法沒法提供異常的捕獲,異常的拋出,可能在建立對象的時候。

【B】靜態塊的實現方式

    靜態塊的實現方式和上面的實現方式差很少,可是靜態塊的實現,能夠處理異常。代碼以下:

public class StaticBlockSingleton
{
    private static StaticBlockSingleton instance;
    private StaticBlockSingleton(){}
    //static block initialization for exception handling
    static
    {
        try{
            instance = new StaticBlockSingleton();
           }catch(Exception e)
           {
                throw new RuntimeException("Exception occured in creating singleton instance");
           }
    }
    public static StaticBlockSingleton getInstance()
    {
        return instance;
    }
}

    和飢餓的實現方式同樣,當類被classloader加載的時候就初始化了該對象。

【C】延遲建立

    顧名思義,就是須要的時候再去建立。

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

    上面這種實現方法,在單線程的環境下是工做良好的,可是,在多線程併發建立對象的時候會出現問題,簡單的分析下緣由,考慮多線程併發安全的時候,首先要找到共享點,就是共享的對象,而後想象各類執行順序,你會發現instance這個地方就是共享點,當多線程執行的時候,有可能會建立多個對象,因此該方法不能保證單例實現。

【D】線程安全的建立

    爲了解決多線程併發建立對象的問題,咱們引入Synchronized關鍵字,這樣的話,同一時刻,只有一個對象能訪問getInstance方法。

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

    這種方式是能防止多併發引發的多對象建立問題,可是寫代碼嘛,總要追求點東西,你說別人裝逼也好,賣弄也罷,總之在某一方面比你好。在多併發中,HashMap和ConcurrentHashMap,你是鎖住整個HashMap仍是HashMap的一個segment這是有質的區別的,因此改進的代碼,只朝着一個方向,那就是下降鎖的力度,因而乎,下面的代碼使用double check來下降了力度,提升了性能。

public static ThreadSafeSingleton getInstanceUsingDoubleLocking()
{
    if(instance == null)
    {
        synchronized (ThreadSafeSingleton.class) 
        {
            if(instance == null)
            {
                instance = new ThreadSafeSingleton();
            }
        }
    }
    return instance;
}

還有一種實現叫Bill Pugh,java的版本更新變化很快,包括新出的java9,可能名字不是,你們也知道,金融在將來幾年是個很火的領域,因而乎java9中集成了貨幣的api支持,這是個人猜想,哈哈,包括先在的招財寶,銅板街,挖財,聽說王福強大神,跳到挖財啦,固然我也是作金融行業的業務,有興趣的能夠留言,聽說要招人。這些題外話,可是不無用處哈,java的內存模型會致使上面的幾種方法,在不少不少線程出現的時候,會出現問題,因而乎,這我的,也就是Bill Pugh本身實現了一下這種方法:

【E】Bill Pugh實現

public class BillPughSingleton 
{
    private BillPughSingleton(){}
    private static class SingletonHelper
    {
        private static final BillPughSingleton INSTANCE = new
        BillPughSingleton(); 
    }
    public static BillPughSingleton getInstance()
    {
        return SingletonHelper.INSTANCE;
    }
}

看代碼發現Bill Pugh是經過私有靜態內部類來實現的,當單例對象被classloader加載的時候,SingletonHelper是不會被加載到內存的,除非有對象調用getInstance方法,這是最經常使用的建立單例對象的方法,由於不須要鎖,已經在多個項目中使用了這種方法,簡單有效。

【F】Enum實現

public enum EnumSingleton 
{
    INSTANCE;
    public static void doSomething()
    {
        //do something
    }
}

    世界上好人和壞人老是都存在的,單例對象也不例外,總有那麼幾種方法是搞破壞的,費盡千辛萬苦,建立的單例,有可能被破壞的。

    【破壞者1】反射

    【破壞者2】序列化

有破壞辦法,就又解決辦法,破壞者1的解決辦法是ENUM,破壞者2的解決辦法是重寫readResolve方法。這兩部分,你說是和反射有關呢,仍是和序列化有關呢,序列化都夠我寫一篇東西的了。下次再說

                                        poke holes in the abstraction,and it starts leaking.

相關文章
相關標籤/搜索