1. 程式人生 > >設計模式之詳解——單例模式

設計模式之詳解——單例模式

單例模式

你要做的只是複製程式碼去一步步驗證,至於原因程式碼中有;不懂可以聯絡我進行交流。 文中每一塊程式碼你可以直接複製貼上,不必要分開復制貼上單獨創捷類去執行。

/**
 * 單例模式總結:
     單例模式優點
     1、某些類建立比較頻繁,對於一些大型的物件,這是一筆很大的系統開銷。

     2、省去了new操作符,降低了系統記憶體的使用頻率,減輕GC壓力。

     3、有些類如交易所的核心交易引擎,控制著交易流程,如果該類可以建立多個的話,系統完全亂了。
 */
public class SingletonDemo {
    /* 持有私有靜態例項,防止被引用,此處賦值為null,目的是實現延遲載入 */
    private static SingletonDemo instance = null;

    /* 私有構造方法,防止被例項化 */
    private SingletonDemo() {
    }


   /*
   // 靜態工程方法,建立例項
   public static SingletonDemo getInstance() {
        if (instance == null) {
            instance = new SingletonDemo();
        }
        return instance;
    }

這個類可以滿足基本要求,但是,像這樣毫無執行緒安全保護的類,如果我們把它放入多執行緒的環境下,肯定就會
出現問題了,如何解決?我們首先會想到對getInstance方法加synchronized關鍵字,我們改成下面這個:
*/

   /*
   // 靜態工程方法,建立例項
   public static synchronized SingletonDemo getInstance() {
        if (instance == null) {
            instance = new SingletonDemo();
        }
        return instance;
    }
但是,synchronized關鍵字鎖住的是這個物件,這樣的用法,在效能上會有所下降,因為每次呼叫getInstance(),
都要對物件上鎖,事實上,只有在第一次建立物件的時候需要加鎖,之後就不需要了,所以,這個地方需要改進。
我們改成下面這個:
*/

    /*
    // 靜態工程方法,建立例項
    public static SingletonDemo getInstance() {
        if (instance == null) {
            synchronized (instance) {
                if (instance == null) {
                    instance = new SingletonDemo();
                }
            }
        }
        return instance;
    }

    似乎解決了之前提到的問題,將synchronized關鍵字加在了內部,也就是說當呼叫的時候是不需要加鎖的,
    只有在instance為null,並建立物件的時候才需要加鎖,效能有一定的提升。但是,這樣的情況,還是有可能
    有問題的,看下面的情況:在Java指令中建立物件和賦值操作是分開進行的,也就是說
    instance = new Singleton();語句是分兩步執行的。但是JVM並不保證這兩個操作的先後順序,也就是說
    有可能JVM會為新的Singleton例項分配空間,然後直接賦值給instance成員,然後再去初始化這個Singleton例項。
    這樣就可能出錯了,我們改成下面這個:
    */

    /*
    // 此處使用一個內部類來維護單例
    private static class SingletonFactory{
        private static SingletonDemo instance = new SingletonDemo();
    }
    // 獲取例項
    public static SingletonDemo getInstance(){
        return SingletonFactory.instance;
    }
     實際情況是,單例模式使用內部類來維護單例的實現,JVM內部的機制能夠保證當一個類被載入的時候,
    這個類的載入過程是執行緒互斥的。這樣當我們第一次呼叫getInstance的時候,JVM能夠幫我們保證instance
    只被建立一次,並且會保證把賦值給instance的記憶體初始化完畢,這樣我們就不用擔心上面的問題。同時該方法
    也只會在第一次呼叫的時候使用互斥機制,這樣就解決了低效能問題。這樣我們暫時總結一個完美的單例模式:
    */
    /*也有人這樣實現:因為我們只需要在建立類的時候進行同步,所以只要將建立和getInstance()分開,
    單獨為建立加synchronized關鍵字,也是可以的:*/
    private static synchronized void syncInit() {
        if (instance == null) {
            instance = new SingletonDemo();
        }
    }

    public static SingletonDemo getInstance() {
        if (instance == null) {
            syncInit();
        }
        return instance;
    }

    /* 如果該物件被用於序列化,可以保證物件在序列化前後保持一致 */
    public Object readResolve() {
        return instance;
    }
}