1. 程式人生 > 實用技巧 >單例模式中指令重排序及需要使用volatile的理解

單例模式中指令重排序及需要使用volatile的理解

建立單例模型的方法有多種,我們常用的是雙重校驗法,程式碼如下:

public class SingleTon {
    private static SingleTon instance = null;
    private SingleTon(){}

    public static SingleTon getInstance(){
        if(instance == null) {//第一次判斷是否為null
            synchronized (SingleTon.class){//使用synchronized對class加鎖
                if(instance == null) {//再次判斷是否為空
                    instance = new SingleTon();
                }
            }
        }
        return instance;
    }
}  

上述程式碼,在執行10萬、20萬次可能沒有問題,但是總有可能會出現一種報null的異常;分析如下:

instance = new SingleTon();

上面的程式碼,在底層其實是分成了3步:

1.分配地址空間
2.初始化SingleTon物件
3.將SingleTon物件的地址賦值給instance,此時instance不為null

我們都知道,jvm為了提高效能,編譯器和處理器都會對指令進行重排序,上述的3步,中2和3是沒有資料依賴,可以重排序,所以可能的執行順序是1/3/2,所以當第一個執行緒在按照1/3/2建立單例時,正好執行到了3步驟,只是將地址空間賦值給instance,而沒有初始化SingleTon物件,第二個執行緒過來以判斷instance不為null,直接就呼叫單例內部其他方法的話可能就會報null

總結,所以在使用雙重校驗建立單例時,instance需要使用volatile來修飾