Android面試問到單例咋整?

前言

單例模式是java設計模式之一。這種模式涉及到一個單一的類,該類負責建立本身的對象,並確保是單一的對象。這個類提供直接訪問其單一對象的方式,且不須要實例化該類的對象。
文末準備了一份完整系統的進階提高的技術大綱和學習資料,但願對於有必定工做經驗可是技術還須要提高的朋友提供一個方向參考,以及免去沒必要要的網上處處搜資料時間精力。java

特色

  1. 單例類只能有一個示例設計模式

  2. 單例類必須本身建立本身惟一的實例。構造函數是私有的,外部是沒法實例化該類。安全

  3. 單例類必須給全部其餘對象提供這一實例。多線程

優缺點

  1. 優勢
  • 減小程序內部實例數目,節省系統資源併發

  • 全局使用的實例能夠避免其頻繁的建立與銷燬函數

  • 避免對資源的多重佔用學習

  1. 缺點
  • 沒有接口,不能繼承spa

  • 與單一職責原則衝突,一個類應該只關心內部邏輯,而不關心外面怎麼樣來實例化線程

實現

  1. 按照概念,咱們實現一下單例模式,以下:
public class SingleInstanceClass {
    //建立本身的對象
    private static SingleInstanceClass instanceClass=new SingleInstanceClass();
    //私有構造方法
    private SingleInstanceClass(){

    }
    //對外提供獲取該類對象的方法
    public static SingleInstanceClass getInstanceClass(){
        return instanceClass;
    }
}

複製代碼

這就完了嗎?設計


那必須接着折騰,上面代碼雖然徹底實現了單例。可是會發現,若是程序中沒有使用到這個對象,他依然會在編譯時建立對應的實例,這樣就浪費了資源。因而便有了懶加載的建立單例方式。

  1. 懶加載模式
    按照上面的分析,爲了避免浪費資源咱們須要在使用給對象的時候再去建立它的實例對象,也就是懶加載模式。看下面的代碼:
public class SingleInstanceClass {
    private static SingleInstanceClass instanceClass;
    //私有構造方法
    private SingleInstanceClass(){

    }
    //對外提供獲取該類對象的方法,且調用此方法時,建立實例
    public static SingleInstanceClass getInstanceClass(){
        //保證惟一性
        if(instanceClass==null){
            instanceClass=new SingleInstanceClass();
        }
        return instanceClass;
    }
}
複製代碼

與放法一不一樣之處在於,只有當咱們使用SingleInstanceClass.getInstanceClass()方法時纔會實例化該對象。可是,若是是在多線程中呢?這種方式又有弊端了,多線程有可能依然會屢次實例化這個對象。那爲解決這個問題咱們來看第三種方式。

  1. 懶加載,線程安全方式
public class SingleInstanceClass {
    private static SingleInstanceClass instanceClass;
    //私有構造方法
    private SingleInstanceClass(){

    }
    //對外提供獲取該類對象的方法,且調用此方法時,建立實例,加入線程鎖
    public static synchronized SingleInstanceClass getInstanceClass(){
        //保證惟一性
        if(instanceClass==null){
            instanceClass=new SingleInstanceClass();
        }
        return instanceClass;
    }
}
複製代碼

synchronized 同步鎖,多線程併發時,同一時間只會執行一個線程。

這種方法由於加了鎖,會致使執行效率變低,因而乎爲了提升運行效率,且又能保證線程安全。又演變出第四中方式。

  1. 雙檢鎖/雙重校驗鎖(DCL,即double-checked locking)
public class SingleInstanceClass {
    private volatile static SingleInstanceClass instanceClass;
    //私有構造方法
    private SingleInstanceClass(){

    }
    //對外提供獲取該類對象的方法,且調用此方法時,建立實例
    public static SingleInstanceClass getInstanceClass(){
        //保證惟一性
        if(instanceClass==null){
          synchronized (SingleInstanceClass.class){
              if(instanceClass==null){
                  instanceClass=new SingleInstanceClass();
              }
          }
        }
        return instanceClass;
    }
}
複製代碼

一旦一個共享變量(類的成員變量、類的靜態成員變量)被volatile修飾以後,那麼就具有了兩層語義:1. 保證了不一樣線程對這個變量進行操做時的可見性,即一個線程修改了某個變量的值,這新值對其餘線程來講是當即可見的。2. 禁止進行指令重排序

除此以外還有其它的方式以下

  1. 登記式/靜態內部類
    這種方法,要比上面的方法實現上面簡單許多,且效果是同樣的。
public class SingleInstanceClass {
    //靜態內部類中建立外部類的實例
    private static class SingleHolder {
        private static SingleInstanceClass INSTANCE = new SingleInstanceClass();
    }

    //私有構造方法
    private SingleInstanceClass() {

    }

    public static SingleInstanceClass getInstance() {
        return SingleHolder.INSTANCE;
    }

}
複製代碼

經驗之談:通常狀況下,不建議使用第 1 種和第 2 種方式,建議使用第 3 種方式。只有在要明確實現 懶加載效果時,纔會使用第 5 種登記方式。若是有其餘特殊的需求,能夠考慮使用第 4 種雙檢鎖方式。


相關文章
相關標籤/搜索