如何寫出一個性能優化的單例模式


單例模型是面試當中最常見的一種設計模式,它是一種對象創建模式,用於產生一個對象的具體實例,可以確保系統中一個類只產生一個實例。

簡而言之,單例模式可以帶來兩個好處:

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM