一、定義
確保某一個類只有一個例項,而且自行例項化並向整個系統提供這個例項。
二。使用場景
當物件需要消耗的資源過多(如訪問Io、資料庫等),避免產生多個物件消耗過多的資源,或者某種型別的物件只應該有且只有一個。
三、實現方法
(1)餓漢單例模式
依靠靜態物件的初始化實現單例
public class Singleton{
private static Singleton mInstance = new Singleton();
private Singleton() {}
public static synchronized Singleton getInstance(){
return mInstance;
}
}
複製程式碼
(2)懶漢單例模式
第一次呼叫getInstance()後,單例物件就會被例項化。但每次呼叫getInstance()方法時都需要進行同步,會造成不必要的消耗。
對於不頻繁呼叫getInstance()的情況下,可以適用。
public class Singleton{
private static Singleton mInstance;
private Singleton() {}
public static synchronized Singleton getInstance(){
if( mInstance == null){
mInstance = new Singleton();
}
return mInstance;
}
}
複製程式碼
(3) Double Check Lock(DCL) 實現單例
public class Singleton{
private volatile static Singleton mInstance;
private Singleton() {}
public static Singleton getInstance(){
if( mInstance == null){
synchronized(Singleton.class){
if( mInstance == null){
mInstance = new Singleton();
}
}
}
return mInstance;
}
}
複製程式碼
進行兩次判空,第一次判斷主要避免不必要的同步,第二次判斷主要為了在同步情況下確認單例仍為空時,進行初始化。
volatile關鍵字的作用:
1、防止重排序
new物件時,會進行三件事件:
(1)、給Singleton的例項分配記憶體;
(2)、呼叫Singleton()的構造方法,初始化成員變數。
(3)、將mInstance物件指向分配的記憶體空間。
而在JVM中(2)和(3)的順序是無法被保證的,只能通過volalite保證其有序性。
2、保證執行緒的可見性。
(4) 靜態內部類單例模型
依靠靜態內部類被使用時才被JVM載入的原理。
public class Singleton{
private Singleton{}
public static Singleton getInstance(){
return SingletonHolder.mInstance;
}
//靜態內部類
private static class SingletonHolder{
private static final Singleton mInstance = new Singleton();
}
}
複製程式碼
靜態內部類和非靜態內部類載入時機的區別:
非靜態內部類,一定需要外部類例項化後才會被載入。
靜態內部類的載入不需要依附外部類,在使用時才載入進。
(5) 使用容器實現單例。
將多種單例型別注入到一個統一的管理類中,使用時根據key獲取對應的單例物件。
public class SingletonManager{
private static Map<String,Object> objMap = new HashMap<String,Object>();
private SingletonManager{}
public static void registerService(String key,Object instance){
if(!object.containsKey(key)){
objMap.put(key,instance);
}
}
public static Object getService(String key){
return objMap.get(key);
}
}
複製程式碼
(6) 列舉單例
列舉型別可以定義自己的方法和自己的變數,而且列舉例項的建立是執行緒安全的,並且任何情況下都是單例。
public enum SingletonEnum{
INSTANCE;
public void doSomething(){
System.out.println("do ");
}
}
複製程式碼