設計模式-單例類

單例(Singleton)模式的定義

指一個類只有一個實例,且該類能自行創建這個實例的一種模式。例如,Windows 中只能打開一個任務管理器,這樣可以避免因打開多個任務管理器窗口而造成內存資源的浪費,或出現各個窗口顯示內容的不一致等錯誤。

在計算機系統中,還有 Windows 的回收站、操作系統中的文件系統、多線程中的線程池、顯卡的驅動程序對象、打印機的後臺處理服務、應用程序的日誌對象、數據庫的連接池、網站的計數器、Web 應用的配置對象、應用程序中的對話框、系統中的緩存等常常被設計成單例。

單例模式有 3 個特點:

單例類只有一個實例對象;
該單例對象必須由單例類自行創建;
單例類對外提供一個訪問該單例的全局訪問點;

其結構如圖 1 所示。
18094074-af4c4f2d1dc1daf6.gif
結構圖
懶漢模式
public class LazySingleton implements Singleton{

    private static volatile LazySingleton instance=null;    //保證 instance 在所有線程中同步
    
    //private 避免類在外部被實例化
    private LazySingleton(){
        show("懶漢創建");
    }    
    
    public static synchronized LazySingleton getInstance()
    {
        //getInstance 方法前加同步
        if(instance==null)
        {
            instance=new LazySingleton();
        }
        return instance;
    }
    public void show(String message) {
        System.out.print(message+"\n");
    }
}

大家看到volatile,synchronized這種修飾詞時,大概就想到有關線程的問題了,沒錯假如我們不加這兩個會如何呢,廢話不多說,直接敲代碼。

public void init() {
  ExecutorService threadpool = Executors.newFixedThreadPool(20);
  for (int i = 0; i < 20; i++) 
  {
        threadpool.execute(new Runnable() {
                public void run() {
          System.out.println(Thread.currentThread().getName() + ":" + LazySingleton.getInstance());
          }
      });
   }
}
懶漢創建
懶漢創建
懶漢創建
pool-1-thread-18:org.design.singleton.mode.LazySingleton@2e509126
懶漢創建
pool-1-thread-17:org.design.singleton.mode.LazySingleton@7153e174
懶漢創建
pool-1-thread-15:org.design.singleton.mode.LazySingleton@6714c37e
pool-1-thread-16:org.design.singleton.mode.LazySingleton@6714c37e
pool-1-thread-20:org.design.singleton.mode.LazySingleton@24c394d2
......

結果就有點扯淡了,完全不符合要求。足以看見其線程安全問題。所以儘管需要消耗些性能但這是必須的。但我們來看看餓漢模式。

餓漢式單例

該模式的特點是類一旦加載就創建一個單例,保證在調用 getInstance 方法之前單例已經存在了。好比如房子建好了,但是還沒裝修我就入住了。不過餓漢式單例在類創建的同時就已經創建好一個靜態的對象供系統使用,以後不再改變,所以是線程安全的,可以直接用於多線程而不會出現問題。

public class HungrySingleton implements Singleton{
   private static final HungrySingleton instance=new HungrySingleton();
    
    private HungrySingleton(){
        System.out.print("餓漢創建\n");
    }
    
    public static HungrySingleton getInstance()
    {
        return instance;
    }
    
    public void show(String message) {
        System.out.print(message+"\n") ;
    }
}

然後有點很不好的是,我還沒用你,你就自己創建了,這對於程序來說一兩個沒啥問題,但是萬一多呢?這就很耗費資源。我們更希望是用到才創建。有沒有這種單例類呢?答案絕對是有的。來看看下面這個例子。

public class StaticSingleton implements Singleton{
    
    private Integer age=20;
    
    private static class InnerSingleton{
        private static StaticSingleton instance=new StaticSingleton();
    }
    
    public StaticSingleton() {
        show("內部靜態創建");
    }
    
    public static StaticSingleton getInstance() {
        return InnerSingleton.instance;
    }

    @Override
    public void show(String message) {
        System.out.print(message+"\n");
    }

}

這就做到我們用到才創建,而且不存在線程安全問題。比較符合我們的預期。

單例模式的應用場景

前面分析了單例模式的結構與特點,以下是它通常適用的場景的特點。
在應用場景中,某類只要求生成一個對象的時候,如一個班中的班長、每個人的身份證號等。
當對象需要被共享的場合。由於單例模式只允許創建一個對象,共享該對象可以節省內存,並加快對象訪問速度。如 Web 中的配置對象、數據庫的連接池等。
當某類需要頻繁實例化,而創建的對象又頻繁被銷燬的時候,如多線程的線程池、網絡連接池等。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章