如何寫出一個性能優化的單例模式
- 2019 年 10 月 5 日
- 筆記
單例模型是面試當中最常見的一種設計模式,它是一種對象創建模式,用於產生一個對象的具體實例,可以確保系統中一個類只產生一個實例。
簡而言之,單例模式可以帶來兩個好處:
1、對於頻繁使用到的對象,可以節省反覆創建對象花費的時間;
2、減少對象的實例化操作,故而可以降低系統記憶體的使用頻率;
根據以上兩點,可看出使用單例模式能夠有效地改善系統的性能。
最常見的單例模式有餓漢模式與懶漢模式。
1、餓漢模式長這樣的:
1 public class Singleton{ 2 3 private Singleton(){} 4 5 private static final Singleton instance=new Singleton(); 6 7 public static Singleton getInstance(){ 8 9 return instance; 10 } 11 }
這種單例模式非常簡單,唯一不足的是,無法對instance實例做延遲載入,由於instance成員變數是static定義的,因此JVM在載入單例類時,單例對象就會被建立,如果這個單例類在系統中還包含了其他的靜態方法,每次通過這個單例類去調用其他的靜態方法時,就會載入被static定義的成員變數,也就是載入了private static final Singleton instance=new Singleton(),故而就會創建一個Singleton實例出來,可以通過一個例子來進行說明:
1 public class Singleton{ 2 3 private Singleton(){ 4 5 System.out.println("創建了一個單例!"); 6 } 7 8 private static final Singleton instance=new Singleton(); 9 10 public static Singleton getInstance(){ 11 12 return instance; 13 } 14 15 public static void Test(){ 16 17 System.out.println("調用了這個方法!"); 18 } 19 20 }
列印出這樣的資訊:

由此可見,餓漢模式因為沒有延遲載入機制,存在著對象容易被創建的問題,這將會影響系統在調用相關函數時的反應速度,可以加入延遲載入機制來解決這個問題。
加了延遲機制的單例模式,就成了我們常見的懶漢模式了,但這裡加了同步安全機制:
1 public class SingletonSyn{ 2 3 private SingletonSyn(){ 4 System.out.println("創建了一個執行緒安全的懶漢單例!"); 5 } 6 7 private static SingletonSyn instance=null; 8 9 public static synchronized SingletonSyn getInstance() { 10 if (instance == null) 11 instance = new SingletonSyn(); 12 return instance; 13 14 } 15 }
這裡需注意的地方是:getInstance()方法必須是同步的,否則在多執行緒環境下,當執行緒1正新建單例時,完成操作賦值時,執行緒2可能判斷instance為null,故執行緒2也將啟動新建單常式序,這樣就會導致多個實例被創建,對性能的影響將會加劇,故加synchronized做同步是必須的。
可謂成也蕭何敗也蕭何,雖然加上了同步關鍵字synchronized 可以解決同步問題,但因在多執行緒的環境下,它的性能消耗將遠遠大於第一種餓漢模式。
基於前面兩種單例模式,可以對它做進一步的改進:
1 public class Singleton{ 2 3 private Singleton(){System.out.println("創建了一個基於內部類的單例模式!");} 4 5 private static class SingletonHolder{ 6 private static Singelton instance=new Singleton(); 7 8 } 9 10 public static Singleton getInstance(){ 11 12 return SingletonHolder.instance; 13 14 } 15 }
上面的例子使用內部類來維護單例的實例,當Singleton被載入時,其內部類並不會被初始化,故可以確保當Singleton類被載入JVM時,不會初始化單例類,只有當getInstance()被調用時,才會載入內部類SingletonHolder,從而實例化單例類instance。同時,由於實例的建立是在類載入時才完成的,故天生對多執行緒友好,getInstance方法也無需使用同步synchronized,可見,使用內部類方式實現單例,既可以做到延遲載入,也不必使用同步關鍵字,是一種比較完善的實現。
當然,若需更加完善單例模式的設計,還有更優的方式,感興趣的夥伴可以繼續深入進行一個探討。
我的部落格即將同步至騰訊雲+社區,邀請大家一同入駐:https://cloud.tencent.com/developer/support-plan?invite_code=2lfj12z61ny88