1. 程式人生 > 程式設計 >雙重檢查鎖定與單例

雙重檢查鎖定與單例

雙重檢查鎖定與單例-原文連結
雙重檢查鎖定與單例-原文連結
雙重檢查鎖定與單例-原文連結

對於單例模式,相信大多數人都可以寫出好幾種實現方法,懶漢,餓漢等等,然而小小單例真要寫好,寫的完全正確也並非易事。

雙重檢查鎖的單例

下面是我們經常使用的一種單例的實現,也就是雙重檢查所的實現方案。

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;        
    }        
} 
複製程式碼

關注我們

關注我們