設計模式之詳解——單例模式
阿新 • • 發佈:2018-12-11
單例模式
你要做的只是複製程式碼去一步步驗證,至於原因程式碼中有;不懂可以聯絡我進行交流。 文中每一塊程式碼你可以直接複製貼上,不必要分開復制貼上單獨創捷類去執行。
/** * 單例模式總結: 單例模式優點 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; } }