線程安全的餓漢模式(強(qiáng)烈推薦)
public class Singleton {
private Singleton(){}
private static final Singleton instance = new Singleton();
public static Singleton getInstance(){
return instance;
}
}
private static final Singleton instance = new Singleton();為什么要用static,因?yàn)間etInstance方法是static的(不用生成實(shí)例就能使用),靜態(tài)方法只能調(diào)用靜態(tài)成員,在類初始化時(shí)就實(shí)例化instance
線程安全的懶漢模式
public class Singleton {//雙重校驗(yàn)的懶漢,且線程安全
private Singleton(){}
private volatile static Singleton instance;(加volatile防止指令重排序)
public static Singleton getInstance(){
if(instance == null){//加鎖效率低,在已經(jīng)生成實(shí)例后,沒(méi)必要再判斷鎖
synchronized(Singleton.class){//加鎖,防止多線程時(shí),生成多個(gè)實(shí)例
if(instance == null){
instance = new Singleton();指令重排序,先完成賦值,但構(gòu)造函數(shù)還沒(méi)執(zhí)行完
}
}
}
return instance;
}
}
private volatile static Singleton instance;添加volatile關(guān)鍵字的原因:
instance = new Singleton();指令重排序,先完成賦值,但構(gòu)造函數(shù)還沒(méi)執(zhí)行完。
instance = new Singleton();可以分解為3行偽代碼
- memory=allocate();// 分配內(nèi)存 相當(dāng)于c的malloc
- ctorInstanc(memory) //初始化對(duì)象
- instance=memory //設(shè)置instance指向剛分配的地址
上面的代碼在編譯器運(yùn)行時(shí),可能會(huì)出現(xiàn)重排序 從1-2-3 排序?yàn)?-3-2
如此在多線程下就會(huì)出現(xiàn)問(wèn)題
例如現(xiàn)在有2個(gè)線程A,B
線程A在執(zhí)行第5行代碼時(shí),B線程進(jìn)來(lái),而此時(shí)A執(zhí)行了 1和3,沒(méi)有執(zhí)行2,此時(shí)B線程判斷instance不為null 直接返回一個(gè)未初始化的對(duì)象,就會(huì)出現(xiàn)問(wèn)題
而用了volatile,上面的重排序就會(huì)在多線程環(huán)境中禁止,不會(huì)出現(xiàn)上述問(wèn)題。