在面試的時候面試官會問我們常用的一些設計模式,這裏先介紹一下單例模式。
- 爲什麼要使用單例模式
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.線程可以看見它將要處理的對象時
這個單例模式是十分常用的,大家一定要清楚其原理再使用。