最簡單的設計模式--單例模式

  我看的設計模式書是《Head First設計模式》,我決定不按照書上的章節順序作筆記,按照我認爲的容易理解程度從易到難來寫。設計模式

  1、單例模式的定義

  單例模式(Singleton Pattern):確保一個類只有一個實例,並提供一個全局訪問點。緩存

  2、實例說明

  有一些對象其實咱們只須要一個,例如:線程池(threadpool)、緩存(cache)、對話框、處理器偏好設置和註冊表(registry)的對象、日誌對象等。多線程

  2.1 一個最簡單的經典單例模式代碼

  咱們能夠用Java的靜態變量來坐到這一點,但事靜態全局變量在程序一開始就被建立好對象,若是這個對象很是耗費資源,而程序在此次執行過程當中又一次沒用到它,就造成浪費。性能

經典的單例模式代碼:spa

 1 public class Singleton {
 2     private static Singleton uniqueInstance;
 3     private Singleton(){}//構造器聲明爲私有,只有類內才能夠調用構造器
 4     public static Singleton getInstance(){
 5         if(uniqueInstance==null){
 6             uniqueInstance=new Singleton();
 7         }
 8         return uniqueInstance;
 9     }
10 }

 

  別的類用要用這個類的對象的話,就經過Singleton.getInstance()來獲取,if判斷並且確保了這個類只有一個實例化的對象。這樣彷佛都一切正常了。不過實際項目中,確定會有多線程的場景,那樣就可能產出兩個實例。例如這裏有兩個線程,線程1運行到上述代碼第5行時候,new 一個對象,假如此刻線程2也進入5行,發現此時uniqueInstance爲null,它也new 一個對象,那麼就產生了兩個實例化對象了。因此,要在第4行上面加上synchronized,這樣就保證了不會存在兩個線程同時進入到getInstance方法。線程

  2.2改善多線程

  在2.1中咱們說過在getInstance方法加入synchronized關鍵字來解決多線程會實例化多個對象的問題,它也存在一些問題:設計

  1)同步會下降性能;日誌

  2)更嚴重的是:上述代碼只要第一次執行getInstance方法時,才真正須要同步。換句話說,一旦設置好uniqueInstance變量,就再也不須要同步這個方法了。以後每次調用這個方法,同步都是一種累贅。code

  下面給出解決三種解決方案:對象

  2.2.1 若是getInstance()的性能對應用程序不是很關鍵,就什麼都別作

   沒錯,就是這麼直接!固然,若是getInstance()在程序中頻繁的運行,那就得從新設計了。

  2.2.2 使用「急切」建立實例,而不用延遲實例化的作法

  若是程序老是會建立並使用到這個單例類,或者建立這個單例類的實例不繁重,能夠急切的建立此單例:

public class Singleton {
    private static Singleton uniqueInstance=new Singleton();//在靜態初始化中建立單例
    private Singleton(){}
    public static Singleton getInstance(){
        return uniqueInstance;
    }
}

  2.2.3 用「雙重檢查加鎖」,在getInstance()中減小使用同步

  首先檢查是否實例已經建立了,若是沒有,「才」進行同步。這樣一來,只有第一次建立會用到同步,這正是咱們想要的。

 1 public class Singleton {
 2     private volatile static Singleton uniqueInstance;
 3     private Singleton() {}
 4     public static Singleton getInstance() {
 5         if (uniqueInstance == null) {
 6             synchronized (Singleton.class){
 7                 if(uniqueInstance == null){//進入區域後,再檢查一次,若是還是null,才建立實例
 8                     uniqueInstance = new Singleton();
 9                 }
10             }
11         }
12         return uniqueInstance;
13     }
14 }

  第7行還須要加入一次判斷,有可能別的線程在此線程拿5-6行的期間,已經實例化了。(ps:個人我的理解)關於volatile關鍵字,不清楚的同窗能夠去查,之後等我看完JVM,會專門寫一篇volatile關鍵字的文章。

  3、單例模式的延伸

  一、據說兩個類加載器可能會各自建立本身的單例對象?

  答:是的。每一個類加載器都定義了一個命名空間,若是有兩個以上的類加載器,不一樣的類加載器可能會同時加載同一個類,從整個程序來看,同一個類會被加載屢次。若是這樣的事情發生在單件上,就會產生多個單例並存的現象。因此,若是程序有個多個類加載器又同時使用了單例模式,能夠這樣解決:自行指定類加載器,並指定同一個類加載器。

  二、全局變量比單例模式差在哪裏?

  答:1)不可延遲實例化;2)並不能確保只有一個實例,並且也變相鼓勵開發人員,用許多全局變量指向許多小對象來形成命名空間的污染。

相關文章
相關標籤/搜索