概念:
作為對象的建立模式,單例模式確定某一個類隻有一個執行個體,而且自行執行個體化,并向整個系統提供這個執行個體。
特點:
1.單例類隻能有一個執行個體
2.單例類必須建立自己的唯一執行個體
3.單例類必須給其他所有對象提供這一執行個體。
餓漢式單例類
public class EagerSingleton {
private static EagerSingleton instance = new EagerSingleton();
/**
* 私有預設構造子
*/
private EagerSingleton(){}
/**
* 靜态工廠方法
*/
public static EagerSingleton getInstance(){
return instance;
}
}
複制
由于選用的是靜态資源,在類加載的時候,靜态變量instance就會被初始化,類的唯一變量也在這時候建立了出來。
餓漢式,顧名思義,急于建立對象,在類加載的時候就完成了對象的建立。
餓漢式是一種采用“空間換取時間”,當類裝載的時候就會建立類的執行個體,不管你用不用,先建立,調用的時候,無需判斷直接使用,節約了時間。
懶漢式單例
public class LazySingleton {
private static LazySingleton instance = null;
/**
* 私有預設構造子
*/
private LazySingleton(){}
/**
* 靜态工廠方法
*/
public static synchronized LazySingleton getInstance(){
if(instance == null){
instance = new LazySingleton();
}
return instance;
}
}
複制
隻有在真正需要的時候才會去建立,采用的是時間換空間,由于是線程安全的,會降低通路速度。固可使用雙檢鎖的方式進行優化。
雙重檢查加鎖
機制:并不是每次進入getInstance方法都需要同步,而不是先同步,進入方法後,先檢查執行個體是否存在,如果不存在才進行下面的同步塊,這是第一檢查,進入同步塊後,在檢查執行個體是否存在,如果不存在,就在同步的情況下建立一個執行個體,這是第二重檢查,這樣一來就隻需要同步一次,減少了多次在同步情況下進行判斷所浪費的時間。
雙檢鎖的關鍵是,使用volatile,它在此處的作用是,該變量将不會被某一個線程緩存,而是在共享記憶體中,被所有進行讀寫的記憶體共享到,進而保證多個線程能夠正确處理該變量。
public class Singleton {
private volatile static Singleton instance = null;
private Singleton(){}
public static Singleton getInstance(){
//先檢查執行個體是否存在,如果不存在才進入下面的同步塊
if(instance == null){
//同步塊,線程安全的建立執行個體
synchronized (Singleton.class) {
//再次檢查執行個體是否存在,如果不存在才真正的建立執行個體
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
複制
注:由于volatile關鍵字會屏蔽虛拟機的一些代碼優化,是以運作效率并不高,所有其實應該盡量避免使用雙檢鎖的方式來實作單例。
懶加載模式(内部類形式)
什麼是内部類:
内部類就是指在類裡面的類,被static修飾的内部類,稱為類級内部類,無static修飾的内部類稱為對象級内部類。
類級内部類是外部類的static部分,它的對象與外部類對象間并沒有依賴關系,是以可以直接建立。而對象級内部類的執行個體,是綁定在外部對象的執行個體中的。
類級内部類中,可以定義靜态方法,在靜态方法中隻能夠引用外部類中的靜态成員或者成員變量。
類級内部類相當于外部類的成員,隻有在第一次被使用的時候才會加載。
多線程預設同步鎖的相關知識:
在下面操作下,jvm會自動為該操作進行同步,以避免出現并發安全問題。
1.由靜态初始化器(在靜态字段上或static{}塊中的初始化器)初始化資料。
2.通路final字段時,
3.在建立線程前建立對象時,
4.線程可以看見它将要處理的對象時。
綜上的解決思路:
采用靜态初始化器的方式通過jvm來保證線程的安全性,采用類級内部類的方式實作延遲加載。
public class Singleton {
private Singleton(){}
/**
* 類級的内部類,也就是靜态的成員式内部類,該内部類的執行個體與外部類的執行個體
* 沒有綁定關系,而且隻有被調用到時才會裝載,進而實作了延遲加載。
*/
private static class SingletonHolder{
/**
* 靜态初始化器,由JVM來保證線程安全
*/
private static Singleton instance = new Singleton();
}
public static Singleton getInstance(){
return SingletonHolder.instance;
}
}
複制
在getInstance第一次被調用時,會讀取内部類的instance,這時,類級内部類完成初始化,進而建立單例,實作了延時加載,由于是靜态成員,隻有在類加載的時候才加載一次,且通過jvm的預設方式實作了線程安全問題。
單例類枚舉:
單元素的枚舉類型已經成為實作Singleton的最佳方法。用枚舉來實作單例非常簡單,隻需要編寫一個包含單個元素的枚舉類型即可。
public enum Singleton {
/**
* 定義一個枚舉的元素,它就代表了Singleton的一個執行個體。
*/
uniqueInstance;
/**
* 單例可以有自己的操作
*/
public void singletonOperation(){
//功能處理
}
}
複制
使用枚舉來實作單執行個體控制會更加簡潔,而且無償地提供了序列化機制,并由JVM從根本上提供保障,絕對防止多次執行個體化,是更簡潔、高效、安全的實作單例的方式。