Android設計模式探討--單例模式

weixin_33859844發表於2017-09-26

核心原則:將建構函式私有化,通過一個靜態內部方法來獲取唯一例項.

單例模式的定義:確保某個類只有一個例項,避免產生多個物件來消耗過度資源。
下面介紹幾種常見實現單例模式的方法。

餓漢模式
它是在宣告該靜態類時該物件已經存在,並且初始化了.

public class Singleton{
  private  static Singleton mInstance = new Singleton();//注意這裡的static,因為下面呼叫獲取到該例項的是靜態方法.
  public static Singleton getInstance() {//靜態方法
    return mInstance;
  }
  private Singleton() {//注意此處的建構函式必須要用private來修飾
  }
}

優點:寫的比較簡單,執行緒安全.
缺點:消耗點資源,因為即使你不用該單例,靜態儲存空間中仍然會分配空間給該例項.

懶漢模式
它是懶載入的模式.用的時候載入,不用的時候不佔用空間

public class Singleton{
private static Singleton mInstance;
public static synchronized Singleton getInstance() {//注意這裡必須要加synchronized,否則多執行緒呼叫就會有問題,獲取到不唯一的例項.
  if(mInstance == null) {
     mInstance =new Singleton();
  }
  return mInstance;
}
}

優點:多執行緒安全,比餓漢模式好點,它是需要的時候才例項化Singleton物件。
缺點:呼叫getInstance的時候因為加了synchronized鎖,所以其他的執行緒呼叫的時候必須等待,這樣造成了不必要的資源開銷。

DCL模式
Double Check Lock 模式,用的時候初始化,而且不需要getInstance方法進行同步

public class Singleton{
    private static volatile Singleton mInstance;//注意volatile這個關鍵字
    public static Singleton getInstance() {
       if(mInstance == null) {//第一次檢查
          synchronized(Singleton.class){//鎖的是Singleton.class檔案確保多執行緒安全
          if(mInstance == null) {//第二次檢查
                mInstance =new Singleton();
           }
      }
   }
    return mInstance;
  }

}

volatile 這個關鍵字必須要用的原因
上面程式碼中mInstance =new Singleton(); 其實並不是一個原子操作,編譯後大致做了下面3件事情
1)給Singleton分配記憶體空間
2)建構函式,初始化成員變數
3)mInstance物件指向Singleton分配出來的儲存空間中

問題在於 Java編譯器允許處理器亂序執行,所以上面的可能是1->3->2 比如兩個執行緒A,B分別呼叫了getInstance 靜態函式,A執行了 1->3->2的順序,當執行3的時候,B執行緒call getInstance 靜態函式獲取到的mInstance是非空的,直接返回,這個時候B執行緒直接用這個例項就會有問題,因為此時該例項還沒有初始化.這樣呼叫的話就會報錯.
基於上面的原因 JDK1.5之後引入了volatile這個關鍵字,使編譯器每次都是按1->2->3順序執行,這樣就不會有多執行緒的問題,執行緒也安全。

DCL的優點:資源的利用率高,只有在使用的時候才初始化對應的空間.而且高效的實現執行緒同步.
DCL的缺點:使用了volatile關鍵字,第一次載入的速度上稍微慢了點.

靜態內部類
它是利用了Java虛擬機器載入類的特性來解決了執行緒安全,資源消耗等問題.

  public  class Singleton {

       private Singleton() {
        }

        public static Singleton getInstance() {
            return SingletonHolder.mInstance;
        }

        public static class SingletonHolder {
            private static final Singleton mInstance = new Singleton()
        }

 }

這種方式, 通過JVM的類載入方式(虛擬機器會保證一個類的初始化在多執行緒環境中被正確的加鎖、同步), 來保證了多執行緒併發訪問的正確性,由於靜態內部類的載入特性--在使用時候才載入,所以實現了懶載入的模式。

優點:
實現執行緒安全,實現懶載入
缺點:
必須依賴Java虛擬機器

列舉單例

public enum Singleton{
INSTANCE;
}

優點:單元素列舉不僅能避免多執行緒同步問題,防止反序列化時重新建立新的物件

Android 用單例的例子

 
public final class InputMethodManager {
static InputMethodManager sInstance;
...
   public static InputMethodManager getInstance() {
        synchronized (InputMethodManager.class) {
            if (sInstance == null) {
                IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE);
                IInputMethodManager service = IInputMethodManager.Stub.asInterface(b);
                sInstance = new InputMethodManager(service, Looper.getMainLooper());
            }
            return sInstance;
        }
    }
....

如InputMethodManager利用了懶載入的模式,但是執行緒不安全.沒有加volatile關鍵字

public static synchronized CalendarDatabaseHelper getInstance(Contextcontext)  
{  
    if (sSingleton == null)  
    {  
       sSingleton = newCalendarDatabaseHelper(context);  
    }  
        return sSingleton;  
}  

採用懶漢模式的CalendarDatabaseHelper類,對Calendar資料庫操作

結論:
1)實現單例模式分3步:構造器私有化,宣告私有靜態變數,提供靜態獲取例項的方法.
2)如果是單執行緒推薦懶載入模式,如果是多執行緒推薦靜態內部類的方式實現單例
3)對資料庫操作,對資源訪問,對IO操作等最好提供單例模式來處理

相關文章