什么是單例趾盐?
概念:確保某一個類只有一個實例搬泥,而且自行實例化并向整個系統(tǒng)提供這個實例。
為什么要用單例唯沮?
許多時候整個系統(tǒng)只需要擁有一個全局對象匪补,這樣有利于我們協(xié)調(diào)系統(tǒng)的整體行為伞辛。單例確保某個類有且只有一個對象,避免產(chǎn)生多個對象消耗過多的資源夯缺;例如蚤氏,創(chuàng)建一個對象需要消耗的資源過多,如要訪問IO和數(shù)據(jù)庫等資源踊兜,這時就要考慮使用單例模式竿滨。
單例的幾種方式
1.餓漢式
public class HungrySingleton {
public static final HungrySingleton sInstance = new HungrySingleton();
private HungrySingleton() {
}
public static HungrySingleton getInstance() {
return sInstance;
}
}
顧名思義,漢子餓了上來就要吃捏境,加載類的時候就new一個對象于游;
- 優(yōu)點:書寫簡單,不會出現(xiàn)線程不安全的問題垫言。
- 缺點:類一旦被使用就會創(chuàng)建實例贰剥,就算你不需要它的實例。例如筷频,使用該單例類的靜態(tài)屬性或者靜態(tài)方法蚌成,ClassLoader開始加載該類的資源,這時候這個類的靜態(tài)屬性sInstance 需要被初始化凛捏,自然就調(diào)用了構(gòu)造方法担忧。為了解決這個問題就出現(xiàn)了懶漢式。
2.懶漢式
public class LazySingleton {
public static LazySingleton sInstance = null;
private LazySingleton() {
}
public static synchronized LazySingleton getInstance() {
if (sInstance == null) {
sInstance = new LazySingleton();
}
return sInstance;
}
}
- 優(yōu)點:一種延時加載的方式坯癣。
- 缺點:為了保證線程安全瓶盛,加了synchronized關(guān)鍵字,導(dǎo)致每次調(diào)用getInstance()方法不管有沒有創(chuàng)建對象都會進行同步示罗,這樣會消耗不必要的資源惩猫。為了解決這個問題出現(xiàn)了DCL方式。
3.DCL
public class DCLSingleton {
public static DCLSingleton sInstance = null;
private DCLSingleton() {
}
public static DCLSingleton getInstance() {
if (sInstance == null) {
synchronized (DCLSingleton.class) {
if (sInstance == null) {
sInstance = new DCLSingleton();
}
}
}
return sInstance;
}
}
(Double Check Lock)雙檢測加鎖方式
- 優(yōu)點:既實現(xiàn)延時加載蚜点,又保證線程安全轧房,也不會有性能問題。
- 缺點:DCL失效問題禽额。
我們先了解一下sInstance = new DCLSingleton()锯厢;這個語句,實際上它總共做了3件事:
(1) 給sInstance的實例分配內(nèi)存脯倒;
(2) 調(diào)用DCLSingleton的構(gòu)造函數(shù)实辑;
(3) 將sInstance對象指向分配的內(nèi)存空間(此時sInstance就不是null了);
但是藻丢,由于Java編譯器允許處理器亂序執(zhí)行剪撬,以及JDK1.5之前JMM(Java Memory Model,即Java內(nèi)存模型)中Cache悠反、寄存器到主內(nèi)存回寫順序的規(guī)定残黑,上面的(2)和(3)的順序是無法保證的馍佑,也就是說(3)可能在(2)之前執(zhí)行,假如線程A先走了(3)梨水,此時sInstance已經(jīng)是非空了拭荤,線程B進來判斷sInstance != null,直接取走sInstance使用疫诽,就會出現(xiàn)空指針問題舅世。
JDK1.5之后,官方注意到這個問題奇徒,調(diào)整了JVM雏亚,具體化了volatile關(guān)鍵字,因此JDK1.5以后的版本可以改成
public static volatile DCLSingleton sInstance = null;
這樣就可以保證sInstance每次都從主內(nèi)存中讀取摩钙。
4.靜態(tài)內(nèi)部類
public class StaticInnerClassSingleton {
private StaticInnerClassSingleton(){}
public static StaticInnerClassSingleton getInstance(){
return SingletonHolder.sInstance;
}
private static class SingletonHolder {
private static StaticInnerClassSingleton sInstance = new StaticInnerClassSingleton();
}
}
首次加載StaticInnerClassSingleton 類時并不會初始化sInstance罢低,只有調(diào)用getInstance()方法時才會加載SingletonHolder類。
- 優(yōu)點:懶加載胖笛、線程安全网持、沒有性能問題。
- 缺點:SingletonHolder類的生命周期比較長匀钧,浪費一丟丟內(nèi)存翎碑。