單例模式

懶漢模式

? 1.將構(gòu)造方法私有化,不允許外部直接創(chuàng)建對象

? 2.聲明類的唯一的實例,使用private?static修飾

? 3.提供一個用于實例的方法,使用public?static修飾

public class Singleton {

private singleton() {

}

private static Singleton instance ;

public Singleton getInstance() {

if (instance = null) {

instance = new Singleton();

}

return instance;

}

}


餓漢模式

1.將構(gòu)造方法私有化,不允許外部直接創(chuàng)建對象

2.創(chuàng)建類的唯一實例

3.提供一個用于獲取實例的方法

public class Singleton {

private Singleton() {

}

private static Singleton instance = new Singleton();

public static Singleton getInstance() {

return instance;

}

}



很多人包括我寫單例的時候,第一想到的就是懶漢式

public class Singleton {

private static Singleton instance;

private Singleton (){}

public static Singleton getInstance() {

if (instance == null) {

instance = new Singleton();

}

return instance;

}

}

代碼很簡單,而且是懶加載,只有調(diào)用getInstance方法是才會初始化。但是這樣是線程不安全的,即當(dāng)多個線程并行調(diào)用getInstance的時候,就會創(chuàng)建多個實例,不能正常工作。

所以這里就有了加鎖方式,將整個getInstance方法設(shè)為同步,添加synchronized關(guān)鍵字。

public class Singleton {

private static Singleton instance;

private Singleton (){}

public static synchronized Singleton getInstance() {

if (instance == null) {

instance = new Singleton();

}

return instance;

}

}

這樣簡單粗暴的方式,雖然做到了線程安全,但導(dǎo)致了同一時間內(nèi)只能有一個線程能夠調(diào)用getInstance方法。

其實我們僅僅需要對初始化的代碼進(jìn)行同步,這就有了雙重檢驗鎖方式。

public class Singleton {

private static Singleton instance;

private Singleton (){}

public static Singleton getInstance() {

if (instance == null) {? ? ? ? ? ? ? //第一次檢查

synchronized (Singleton.class) {

if(instance == null) {? ? ? //第二次檢查

instance = new Singleton();

}

}

}

return instance;

}

}

這里第二次檢查,是因為如果有多個線程同時執(zhí)行完了第一次檢查,這時如果同步塊內(nèi)不進(jìn)行第二次檢查的話,會生成多個實例了。

但是看了相關(guān)資料后,發(fā)現(xiàn)這樣還是有點問題。引用資料中的介紹:

由于instance = new Singleton(),這并非是一個原子操作,事實上在 JVM 中這句話大概做了下面 3 件事情。

1.給 instance 分配內(nèi)存

2.調(diào)用 Singleton 的構(gòu)造函數(shù)來初始化成員變量

3.將instance對象指向分配的內(nèi)存空間(執(zhí)行完這步 instance 就為非 null 了)

但是在 JVM 的即時編譯器中存在指令重排序的優(yōu)化。也就是說上面的第二步和第三步的順序是不能保證的,最終的執(zhí)行順序可能是 1-2-3 也可能是 1-3-2。如果是后者,則在 3 執(zhí)行完畢、2 未執(zhí)行之前,被線程二搶占了,這時 instance 已經(jīng)是非 null 了(但卻沒有初始化),所以線程二會直接返回 instance,然后使用,然后順理成章地報錯。

我們只需要將 instance 變量聲明成 volatile 就可以了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容