單例模式介紹
單例模式是使用最廣泛的一種設(shè)計模式刑巧,在這種設(shè)計模式中,單例對象的類必須保證有且只有一種實力存在逆皮。在我們的應(yīng)用中往往會存在這么一個全局對象宅粥,用來統(tǒng)一處理某種行為,如網(wǎng)絡(luò)請求电谣、數(shù)據(jù)緩存秽梅、圖片加載等。像這種情況就沒必要每次使用時都構(gòu)造實例剿牺,而往往采用單例模式
應(yīng)用場景
某種類型的對象有且只有一個對象企垦,避免產(chǎn)生多個對象消耗更多的資源。
實現(xiàn)單例模式有如下關(guān)鍵點:
- 構(gòu)造函數(shù)不對外開放晒来,一般都是私有的钞诡。
- 對外提供一個靜態(tài)的公共方法或枚舉返回單例對象。
- 確保單例對象有且只有一個湃崩,如多線程情況下荧降。
- 確保單例對象在反序列化時不會重新創(chuàng)建對象。
單例模式的實現(xiàn)方式
餓漢模式:這是最常見也是最簡單的是想方式攒读,在調(diào)用getInstance前朵诫,在聲明靜態(tài)對象時就進(jìn)行了初始化。
public class Singleton {
private static Singleton instance=new Singleton();
private Singleton(){ }
public static Singleton getInstance(){
return instance;
}
}
懶漢模式:相對于餓漢不同的是薄扁,在用戶第一次調(diào)用getInstance時才進(jìn)行初始化剪返,實現(xiàn)方式如下
public class Singleton {
private static Singleton instance=new Singleton();
private Singleton(){ }
public synchronized static Singleton getInstance(){
if(instance==null) {
instance=new Singleton();
}
return instance;
}
}
通過觀察在getInstance方式中添加了synchronized關(guān)鍵字废累,也就是說getInstance是個同步方法,保證在多線程情況下保證了單例的唯一性脱盲。但是這里有個問題——即便instance已經(jīng)初始化了邑滨,每次調(diào)用getInstance方法還是要進(jìn)行同步,而方法同步都會消耗資源宾毒,造成不必要的開銷驼修。這也是懶漢單例模式存在的一個最大問題。
Double check Lock(雙重校驗):這種方式是在懶漢诈铛、餓漢式的基礎(chǔ)的升級,既能夠在需要時才初始化墨礁,又能保證線程安全幢竹,而且單例對象初始化后不需要進(jìn)行同步鎖。
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;
}
}
DCL的特點在getInstance方法中的兩次判空:
第一次判斷主要是避免不必要的同步鎖恩静;
第二次判斷是在null情況下創(chuàng)建實例焕毫;
在 instance=new Singleton();這條語句里,大致做了三件事:
- 給Singleton的實例分配內(nèi)存
- 調(diào)用Singleton的構(gòu)造函數(shù)
- 將instance對象指向分配的內(nèi)存空間
如果按照正常的1驶乾、2邑飒、3步驟順利執(zhí)行,那我們能順利獲得實例對象级乐。但是 編譯器為了優(yōu)化程序指令加快cpu處理速度疙咸,會有指令重排序——也就是說上面的三步可能會1、3风科、2順序撒轮。如果線程A執(zhí)行到3完畢、2未完成之前贼穆。被切換到線程B题山,這個時候instance因為在線程A內(nèi)已經(jīng)執(zhí)行了3,這個實際instance則不會為空故痊。所以線程B直接獲取instance顶瞳,在使用的時候就會報錯。
這時只需將instance的定義改為private volatile static Singleton instance = null;就可以保證instance對象每次都要從主內(nèi)存中獲取愕秫。
靜態(tài)內(nèi)部類單例模式
當(dāng)?shù)谝淮渭虞dSingleton類時并不會初始化instance慨菱,只有在第一次調(diào)用Singleton的getInstance方法才會導(dǎo)致instance被初始化。因此第一次調(diào)用getInstance方法會導(dǎo)致虛擬機(jī)加載SingletonHolderl類豫领,同時保證了延遲加載抡柿、線程安全、對象的唯一性等恐,所以推薦使用這種方式實現(xiàn)單例模式洲劣。
public class Singleton {
private Singleton(){ }
public static Singleton getInstance(){
return SingletonHolder.instance;
}
private static class SingletonHolder{
private static final Singleton instance=new Singleton();
}
}
枚舉單例
public enum SingletonEnum {
INSTANCE;
public void doSomeThing(){
System.out.println("do sth");
}
}
容器單例模式
public class SingletonManager {
private static Map<String,Object> objectMap= new HashMap<>();
private SingletonManager(){}
public static void registerService(String key,Object instance){
objectMap.put(key,instance);
}
public static Object getService(String key){
return objectMap.get(key);
}
}