單例模式中指令重排序及需要使用volatile的理解
阿新 • • 發佈:2020-12-19
建立單例模型的方法有多種,我們常用的是雙重校驗法,程式碼如下:
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來修飾