Android面試設計模式之單例模式

在面試的時候面試官會問我們常用的一些設計模式,這裏先介紹一下單例模式。

  • 爲什麼要使用單例模式

1.控制資源的使用,通過線程同步來控制資源的併發訪問;
2.控制實例產生的數量,達到節約系統資源;
3.作爲通訊媒介使用,也就是數據共享,它可以在不建立直接關聯的條件下,讓多個不相關的兩個線程或者進程之間實現通訊。

  • 有多少種單例模式

1.餓漢式

public class CustomSingleton {
    private static CustomSingleton instance = new CustomSingleton();

    /**
     * 私有默認構造子
     */
    private CustomSingleton() {
    }

    public static CustomSingleton getInstance() {
        return instance;
    }
}

上面的例子中,這個類被加載的時候,靜態變量instance會被初始化,此時類的私有構造方法會調用。此時,單例的實例就會被創建出來。
其實我們很容易理解,餓漢式從字面上就能理解,這個類很餓,不管你用不用,在裝載類的時候就創建對象的實例。

2.懶漢式

public class CustomSingleton {
    private static CustomSingleton instance = null;

    /**
     * 私有默認構造子
     */
    private CustomSingleton() {
    }

    public static synchronized CustomSingleton getInstance() {
        if (null == instance)
            instance = new CustomSingleton();

        return instance;
    }
}

上面的寫法說明懶,每次實例化都會進行判斷,看是否需要去創建實例。由於懶漢式的實現線程安全的,這樣會降低整個訪問的速度,好的我們繼續接着看。

3.雙重檢查加鎖

public class CustomSingleton {
    private static CustomSingleton instance = null;

    /**
     * 私有默認構造子
     */
    private CustomSingleton() {
    }

    public static CustomSingleton getInstance() {
        if (null == instance) {
            synchronized (CustomSingleton.class) {
                if (null == instance) {
                    instance = new CustomSingleton();
                }
            }
        }
        return instance;
    }
}

雙重檢查加鎖。雙重,大家都可以看到先進行第一重檢查,判斷實例是否存在,再進入同步代碼塊,然後第二次檢查實例是否存在,如果不存在,就是同步的情況下創建一個實例,這樣就可以不用在多次創建時在同步情況下進行判斷所浪費的時間。這個實現線程安全,同時不太影響性能。

4.Lazy initialization holder class 靜態內部類

public class CustomSingleton {

    private CustomSingleton() {
    }

    private static class CustomSingletonHolder {
        private static CustomSingleton instance = new CustomSingleton();
    }

    public static CustomSingleton getInstance() {
        return CustomSingletonHolder.instance;
    }
}

思路
 要想很簡單地實現線程安全,可以採用靜態初始化器的方式,它可以由JVM來保證線程的安全性。比如前面的餓漢式實現方式。但是這樣一來,不是會浪費一定的空間嗎?因爲這種實現方式,會在類裝載的時候就初始化對象,不管你需不需要。
 如果現在有一種方法能夠讓類裝載的時候不去初始化對象,那不就解決問題了?一種可行的方式就是採用類級內部類,在這個類級內部類裏面去創建對象實例。這樣一來,只要不使用到這個類級內部類,那就不會創建對象實例,從而同時實現延遲加載和線程安全。

代碼中,當getInstance方法第一次被調用的時候,它第一次讀取SingletonHolder.instance,導致SingletonHolder類得到初始化;而這個類在裝載並被初始化的時候,會初始化它的靜態域,從而創建Singleton的實例,由於是靜態的域,因此只會在虛擬機裝載類的時候初始化一次,並由虛擬機來保證它的線程安全性。
這個模式的優勢在於,getInstance方法並沒有被同步,並且只是執行一個域的訪問,因此延遲初始化並沒有增加任何訪問成本。

補相關基礎知識:
什麼是類級內部類
簡單點說,類級內部類指的是,有static修飾的成員式內部類。如果沒有static修飾的成員式內部類被稱爲對象級內部類。
類級內部類相當於其外部類的static成分,它的對象與外部類對象間不存在依賴關係,因此可直接創建。而對象級內部類的實例,是綁定在外部對象實例中的。
類級內部類中,可以定義靜態的方法。在靜態方法中只能夠引用外部類中的靜態成員方法或者成員變量。
類級內部類相當於其外部類的成員,只有在第一次被使用的時候才被會裝載。

多線程缺省同步鎖的知識
  大家都知道,在多線程開發中,爲了解決併發問題,主要是通過使用synchronized來加互斥鎖進行同步控制。但是在某些情況中,JVM已經隱含地爲您執行了同步,這些情況下就不用自己再來進行同步控制了。這些情況包括:
  1.由靜態初始化器(在靜態字段上或static{}塊中的初始化器)初始化數據時
  2.訪問final字段時
  3.在創建線程之前創建對象時
  4.線程可以看見它將要處理的對象時


這個單例模式是十分常用的,大家一定要清楚其原理再使用。

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