Android設計模式之單例模式的七種寫法

來源:互聯網
上載者:User

標籤:

一 單例模式介紹及它的使用情境

單例模式是應用最廣的模式,也是我最Crowdsourced Security Testing道的一種設計模式,在深入瞭解單例模式之前,每當遇到如:getInstance()這樣的建立執行個體的代碼時,我都會把它當做一種單例模式的實現。其實經常使用的圖片載入架構ImageLoader的執行個體建立就是使用了單例模式,因為這個ImageLoader中含有線程池、緩衝系統、網路請求,很消耗資源,不應該建立多個對象,這時候就需要用到單例模式。

ImageLoader的建立代碼如下:

ImageLoader.getInstance();// 在自己的Application中建立全域執行個體.....//getInstance()執行的源碼 public static ImageLoader getInstance() {        if(instance == null) {//雙重校正DCL單例模式            Class var0 = ImageLoader.class;            synchronized(ImageLoader.class) {//同步代碼塊                if(instance == null) {                    instance = new ImageLoader();//建立一個新的執行個體                }            }        }        return instance;//返回一個執行個體    }

因此,在我們建立一個對象需要消耗過多的資源時,便可以考慮使用單例模式。

二 單例模式的結構圖以及建立的關鍵點?

單例模式的定義是它應該保證一個類僅有一個執行個體,同時這個類還必須提供一個訪問該類的全域訪問點。如是單例模式的結構圖:

實現單例模式有以下幾個關鍵點:
(1)其建構函式不對外開放,一般為private;
(2)通過一個靜態方法或者枚舉返回單例類對象;
(3)確保單例類的對象有且只有一個,尤其要注意多線程的情境;
(4)確保單例類對象在還原序列化時不會重新建立對象;

通過將單例類的建構函式私人化,使得用戶端不能通過new的形式手動構造單例類的對象。單例類會主動暴露一個公有的靜態方法,用戶端調用這個靜態方法擷取到單例類的唯一執行個體。在擷取這個單例類的時候需要確保這個過程是安全執行緒的。

三 單例模式的七種實現方式

(1)懶漢式(線程不安全)

//懶漢式單例類.在第一次調用的時候執行個體化自己   public class Singleton {      //私人的建構函式    private Singleton() {}     //私人的靜態變數     private static Singleton single=null;      //暴露的公有靜態方法       public static Singleton getInstance() {           if (single == null) {                 single = new Singleton();           }            return single;      }  }

懶漢式(線程不安全)的單例模式分為三個部分:私人的構造方法,私人的全域靜態變數,公有的靜態方法。

其中起到重要作用的是靜態修飾符static關鍵字,我們知道在程式中,任何變數或者代碼都是在編譯時間由系統自動分配記憶體來儲存的,而所謂靜態就是指在編譯後所分配的記憶體會一直存在,直到程式退出記憶體才會釋放這個空間,因此也就保證了單例類的執行個體一旦建立,便不會被系統回收,除非手動設定為null。

這種方式建立的缺點是存線上程不安全的問題,解決的辦法就是使用synchronized 關鍵字,便是單例模式的第二種寫法了。

(2)懶漢式(安全執行緒)

public class Singleton {      //私人的靜態變數    private static Singleton instance;      //私人的構造方法    private Singleton (){};    //公有的同步靜態方法    public static synchronized Singleton getInstance() {      if (instance == null) {          instance = new Singleton();      }      return instance;      }  }  

這種單例實現方式的getInstance()方法中添加了synchronized 關鍵字,也就是告訴Java(JVM)getInstance是一個同步方法。

同步的意思是當兩個並發線程訪問同一個類中的這個synchronized同步方法時, 一個時間內只能有一個線程得到執行,另一個線程必須等待當前線程執行完才能執行,因此同步方法使得安全執行緒,保證了單例只有唯一個執行個體。

但是它的缺點在於每次調用getInstance()都進行同步,造成了不必要的同步開銷。這種模式一般不建議使用。

(3)餓漢模式(安全執行緒)
代碼實現如下:

//餓漢式單例類.在類初始化時,已經自行執行個體化   public class Singleton {      //static修飾的靜態變數在記憶體中一旦建立,便永久存在    private static Singleton instance = new Singleton();      private Singleton (){}      public static Singleton getInstance() {      return instance;      }  } 

餓漢式在類建立的同時就已經建立好一個靜態對象供系統使用,以後不再改變,所以天生是安全執行緒的。其中instance=new Singleton()可以寫成:

 static {      instance = new Singleton();      }  

屬於變種的餓漢單例模式,也是基於classloder機制避免了多線程的同步問題,instance在類裝載時就執行個體化了。

(4)DCL雙重校正模式

public class Singleton {      private static Singleton singleton;  //靜態變數    private Singleton (){}  //私人建構函式    public static Singleton getInstance() {        if (singleton == null) {  //第一層校正          synchronized (Singleton.class) {            if (singleton == null) {  //第二層校正              singleton = new Singleton();            }          }        }      return singleton;      }  }  

這種模式的亮點在於getInstance()方法上,其中對singleton 進行了兩次判斷是否空,第一層判斷是為了避免不必要的同步,第二層的判斷是為了在null的情況下才建立執行個體。具體我們來分析一下:

假設線程A執行到了singleton = new Singleton(); 語句,這裡看起來是一句代碼,但是它並不是一個原子操作,這句代碼最終會被編譯成多條彙編指令,它大致會做三件事情:
(a)給Singleton的執行個體分配記憶體
(b)調用Singleton()的建構函式,初始化成員欄位;
(c)將singleton對象指向分配的記憶體空間(即singleton不為空白了);

但是由於Java編譯器允許處理器亂序執行,以及在jdk1.5之前,JMM(Java Memory Model:java記憶體模型)中Cache、寄存器、到主記憶體的回寫順序規定,上面的步驟b 步驟c的執行順序是不保證了。也就是說執行順序可能是a-b-c,也可能是a-c-b,如果是後者的指向順序,並且恰恰在c執行完畢,b尚未執行時,被切換到線程B中,這時候因為singleton線上程A中執行了步驟c了,已經非空了,所以,線程B直接就取走了singleton,再使用時就會出錯。這就是DCL失效問題。
但是在JDK1.5之後,官方給出了volatile關鍵字,將singleton定義的代碼改成:

private volatile static Singleton singleton;  //使用volatile 關鍵字

這樣就解決了DCL失效的問題。

(5)靜態內部類單例模式

public class Singleton {      private Singleton (){} ;//私人的建構函式    public static final Singleton getInstance() {          return SingletonHolder.INSTANCE;      }      //定義的靜態內部類    private static class SingletonHolder {          private static final Singleton INSTANCE = new Singleton();  //建立執行個體的地方    }  }  

當第一次載入Singleton 類的時候並不會初始化INSTANCE ,只有第一次調用Singleton 的getInstance()方法時才會導致INSTANCE 被初始化。因此,第一次調用getInstance()方法會導致虛擬機器載入SingletonHolder 類,這種方式不僅能夠確保單例對象的唯一性,同時也延遲了單例的執行個體化。

(6)枚舉單例

前面的幾種單例模式實現方式,一般都會稍顯麻煩,或是在某些特定的情況下出現一些狀況。下面介紹枚舉單例模式的實現:

public enum Singleton {  //enum枚舉類    INSTANCE;      public void whateverMethod() {      }  }

枚舉單例模式最大的優點就是寫法簡單,枚舉在java中與普通的類是一樣的,不僅能夠有欄位,還能夠有自己的方法,最重要的是預設枚舉執行個體是安全執行緒的,並且在任何情況下,它都是一個單例。即使是在還原序列化的過程,枚舉單例也不會重建新的執行個體。而其他幾種方式,必須加入如下方法:

private Object readResolve()  throws ObjectStreamException{    return INSTANCE;}

才能保證還原序列化時不會產生新的對象。

(7)使用容器實現單例模式
除了上述幾種常見的實現單例的方式,還有另一類的實現,代碼如下:

public class SingletonManager {   private static Map<String, Object> objMap = new HashMap<String,Object>();//使用HashMap作為緩衝容器  private Singleton() {   }  public static void registerService(String key, Objectinstance) {    if (!objMap.containsKey(key) ) {      objMap.put(key, instance) ;//第一次是存入Map    }  }  public static ObjectgetService(String key) {    return objMap.get(key) ;//返回與key相對應的對象  }}

在程式的初始,將多種單例模式注入到一個統一的管理類中,在使用時根據key擷取對應類型的對象。

在Android源碼中,APP啟動的時候,虛擬機器第一次載入該類時會註冊各種ServiceFetcher,比如LayoutInflater Service。將這些服務以索引值對的形式儲存在一個HashMap中,使用者使用時只需要根據key來擷取到對應的ServiceFetcher,然後通過ServiceFetcher對象的getService函數擷取具體的服務物件。當第一次擷取時,會調用ServiceFetcher的creatService函數建立服務物件,然後將該對象緩衝到一個列表中,下次再取時直接從緩衝中擷取,避免重複建立對象,從而達到單例的效果。Android中的系統核心服務以單例形式存在,減少了資源消耗。

總結:不管以哪種形式實現單例模式,它們的核心原理是將建構函式私人化,並且通過靜態公有方法擷取一個唯一的執行個體,在這個擷取的過程中必須保證線程的安全,同時也要防止還原序列化導致重建執行個體對象。

Android設計模式之單例模式的七種寫法

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.