懶漢式
加方法鎖
public class Singleton {
private static Singleton singleton = null;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (Objects.isNull(singleton)) {
singleton = new Singleton();
}
return singleton;
}
}
- 直接在 getInstance() 方法加鎖形葬,但是加鎖的范圍太大,性能低下
雙重檢查鎖定
public class Singleton {
private static volatile Singleton singleton = null;
private Singleton() {
}
public static Singleton getInstance() {
// 1
if (Objects.isNull(singleton)) {
synchronized(Singleton.class) {
if (Objects.isNull(singleton)) {
// 2
singleton = new Singleton();
}
}
}
return singleton;
}
}
對象需要加volatile 關(guān)鍵字,主要是防止指令重排序魄藕。
singleton = new Singleton();
方法在執(zhí)行的時候有三個指令:memory = allocate(); // 1:分配對象的內(nèi)存空間 ctorInstance(memory); // 2:初始化對象 instance = memory; // 3:設(shè)置instance指向剛分配的內(nèi)存地址
當(dāng)線程A獲取到鎖典徊,執(zhí)行初始化的時候發(fā)生了指令重排杭煎,1->3->2。當(dāng)2還沒有被執(zhí)行時卒落,線程B執(zhí)行到代碼標(biāo)記1的位置羡铲,這時判斷到對象不為空,直接返回該對象儡毕,但是這個時候該對象可能還并沒有完成初始化也切,導(dǎo)致線程B在執(zhí)行過程中拋錯。
靜態(tài)內(nèi)部類
public class Singleton {
private static class LazyHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton() {
}
public static Singleton getInstance() {
return LazyHolder.INSTANCE;
}
}
既實(shí)現(xiàn)了線程安全腰湾,又避免了同步帶來的性能影響雷恃。JVM在類的初始化階段(即在Class被加載后,且被線程使用之前)费坊,會執(zhí)行類的初始化倒槐。在 執(zhí)行類的初始化期間,JVM會去獲取一個鎖附井。這個鎖可以同步多個線程對同一個類的初始化讨越。使用這種方式,我們是允許
new Singleton();
過程發(fā)生指令重排的永毅。
使用枚舉的形式
public class EnumSingleton {
private EnumSingleton() {
}
public static EnumSingleton getInstance() {
return Singleton.INSTANCE.getInstance();
}
private enum Singleton {
INSTANCE(new EnumSingleton());
private EnumSingleton singleton;
//JVM會保證此方法絕對只調(diào)用一次
Singleton(EnumSingleton singleton) {
this.singleton = singleton;
}
public EnumSingleton getInstance() {
return singleton;
}
}
}
JVM會保證枚舉類構(gòu)造方法絕對只調(diào)用一次把跨,所以保證了對象實(shí)例的唯一性
餓漢式
public class Singleton {
private static final Singleton singleton = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return singleton;
}
}