1 單例模式
單例模式:確保某一個類只有一個實例,自行實例化并向整個系統(tǒng)提供這個實例怯伊。
使用場景
確保某個類只有一個對象的場景肆糕,避免產(chǎn)生多個對象消耗資源,或者某個對象應(yīng)該有且只有一個的場景篙议。
特點
1.構(gòu)造函數(shù)不對外開放唾糯,一般為private
2.通過靜態(tài)方法返回單例類的對象
3.確保對象只有一個,尤其是在多線程的情況下
4.確保單例類對象在反序列化時不會重新創(chuàng)建對象
寫單例
一:餓漢模式
public class Singleton {
private static final Singleton singleton = new Singleton();
private Singleton(){
}
public static Singleton getInstance(){
return singleton;
}
// do.....
}
二:懶漢模式
public class SingletonV2 {
private static SingletonV2 singletonV2 = null;
private SingletonV2(){
}
public static synchronized SingletonV2 getInstance(){
if(singletonV2 != null){
singletonV2 = new SingletonV2();
}
return singletonV2;
}
// do....
}
優(yōu)點:只有在使用時才會被實例化
缺點:每次使用都要進(jìn)行同步鬼贱,即使單例已經(jīng)存在移怯,造成不必要的同步開銷
三:DCL實現(xiàn)單例(雙鎖)
public class SingletonV3 {
private static SingletonV3 singletonV3 = null;
private SingletonV3(){
}
public static SingletonV3 getInstance(){
if(singletonV3 == null){
synchronized (SingletonV3.class){
if(singletonV3 == null){
singletonV3 = new SingletonV3();
}
}
}
return singletonV3;
}
// do ....
}
DCL失效問題:
singletonV3 = new SingletonV3(); 一句代碼,卻不是原子性操作这难,會被編譯成多條匯編指令舟误,但大致作了三件事:
1.給SingletonV3的實例分配內(nèi)存;
2.調(diào)用SingletonV3的構(gòu)造函數(shù)姻乓,初始化字段嵌溢;
3.將singletonV3指向分配的內(nèi)存空間(這時singletonV3 不為 null)
JDK1.5之前java編譯器允許處理器亂序執(zhí)行,上面2蹋岩,3的執(zhí)行順序是無法保證的赖草,所以執(zhí)行順序可能是1-2-3,也可能是1-3-2剪个,如果是后者疚顷,則有可能出現(xiàn)3執(zhí)行完畢,2還未執(zhí)行禁偎,此時singletonV3已經(jīng)不為null腿堤,但另一個線程取走singletonV3,使用就會出錯如暖。
jdk1.5之后笆檀,使用volatile,修改為private volatile static SingletonV3 singletonV3 = null;就可以保證singletonV3保證每次都是從主內(nèi)存中讀取盒至。
能夠在絕大多數(shù)情況保證單例對象的唯一性酗洒,除非在并發(fā)場景比較復(fù)雜或JDK低于6的版本下使用。
四:靜態(tài)內(nèi)部類實現(xiàn)
public class SingletonV4 {
private SingletonV4(){
}
public static SingletonV4 getInstance(){
return SingletonHolder.singletonV4;
}
private static class SingletonHolder{
private static final SingletonV4 singletonV4 = new SingletonV4();
}
}
第一次加載SingletonV4時并不會初始化singletonV4枷遂,只有第一次調(diào)用getInstance()才會導(dǎo)致加載SingletonHolder類樱衷,線程安全,而且保證對象的唯一性酒唉,切延遲實例化矩桂,推薦的單例實現(xiàn)方式。
五:枚舉實現(xiàn)單例
public enum SIngletonEnum {
Instance;
// do something
private int num = 10;
public int doSomething(){
return num;
}
//
}
枚舉單例的最大特點就是簡單痪伦,枚舉不僅能夠有字段侄榴,還能夠有方法雹锣,枚舉單例是線程安全的。
反序列化
序列化操作提供了一個很特別的鉤子(hook)-類中具有一個私有的被實例化的方法readresolve(),這個方法可以確保類的開發(fā)人員在序列化將會返回怎樣的object上具有發(fā)言權(quán)癞蚕。
private Object readResolve() throws ObjectStreamException {
return getInstance();
}