雙重檢查鎖定與單例
阿新 • • 發佈:2019-12-31
雙重檢查鎖定與單例-原文連結
雙重檢查鎖定與單例-原文連結
雙重檢查鎖定與單例-原文連結
對於單例模式,相信大多數人都可以寫出好幾種實現方法,懶漢,餓漢等等,然而小小單例真要寫好,寫的完全正確也並非易事。
雙重檢查鎖的單例
下面是我們經常使用的一種單例的實現,也就是雙重檢查所的實現方案。
public class Singleton {
private static Singleton instance;
private Singleton() {
}
public Singleton getInstance() {
if (null == instance) {
synchronized (Singleton.class) {
if (null == instance) {
instance = new Singleton(); // error
}
}
}
return uniqueSingleton;
}
}
複製程式碼
讓我們來看一下這個程式碼是如何工作的:首先當一個執行緒發出請求後,會先檢查instance是否為null,如果不是則直接返回其內容,這樣避免了進入synchronized塊所需要花費的資源。其次,如果兩個執行緒同時進入了第一個if判斷,那麼他們也必須按照順序執行 synchronized 塊中的程式碼,第一個進入程式碼塊的執行緒會建立一個新的 Singleton 例項,而後續的執行緒則因為無法通過if判斷,而不會建立多餘的例項。
但還有一個問題,在有些情況下,通過這種方式拿到的Singleton物件,可能是錯誤的 。
回顧我們new物件的3個步驟
-
1,分配記憶體空間
-
2,初始化物件
-
3,將物件指向剛分配的記憶體空間
但jvm在指令優化時,會出現步驟2和3對調的情況,比如執行緒1在經過倆層為 null 判斷後,進入 new 的動作,在還沒有初始化物件時,就返加了地址值,執行緒2在第一個為 null 判斷時,因為物件已經不為空,那麼就直接返回了物件。然而當執行緒2打算使用Singleton例項,卻發現它沒有被初始化,於是錯誤發生了。
解決方案
對於上面的問題,有兩種解決方案
1,使用 volatile 關鍵詞主要可以保證程式碼的執行順序不受 jvm 重排序影響。
public class Singleton {
private volatile static Singleton instance;
private Singleton() {
}
public Singleton getInstance() {
if (null == instance) {
synchronized (Singleton.class) {
if (null == instance) {
instance = new Singleton(); // error
}
}
}
return instance;
}
}
複製程式碼
2,通過內部類實現多執行緒環境中的單例模式。
public class Singleton {
private Singleton() {
}
private static class SingletonContainer {
private static Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return SingletonContainer.instance;
}
}
複製程式碼