單例雙重校驗及IF語句的位元組碼層面的原因理解

秋褲Boy發表於2019-01-21

首先是位元組碼檔案生成。這裡注意,在呼叫javap 檢視位元組碼指令的時候,要指定-verbose引數,不然檢視到資訊不完全,只有類的基礎資訊。

image
一段如下的JAVA程式碼,

public class TestP {
    private int n;
    public TestP(int n) {
        this.n = n;
    }
    private void ifP() {
        int n = 2;
        if (n >1){
            TestP s = new TestP(1);
            System.out.println(s);
        }
    }
}
複製程式碼

編譯後,如下:

image
image
image
image
看圖中 IfP()方法的位元組碼部分。

if_icmple
複製程式碼

就是if語句的比較部分,緊接著下面的幾條指令,

new invokespecial
複製程式碼

兩條指令,

new
複製程式碼

是用來

建立一個物件, 並將其引用引用值壓入棧頂

invokespecial #4
複製程式碼

是用來

用於呼叫一些需要特殊處理的例項方法,包括例項初始化方法、 私有方法和父類方法。 #4處就是超類例項方法跟類例項方法, 後面的部分還有指令是用來 將物件引用賦值給變數的 所以,這裡可以看出,一條 TestP p = new Testp(1);

有3部分指令共同完成。那JVM保證的是最終結果的正確性,並不會保證位元組碼的執行順序,也就是說,很有可能不是按上述順序執行位元組碼指令的,那如此的話,假如例項化初始方法的呼叫晚於synchronized關鍵字後,那就會造成非同步的另一條執行緒在當前執行緒初始化單例後仍然進入第二個if 判null 的語法塊內的情況,這樣就錯了。

所以,雙重校驗的單例模式,一定要用volitile修飾單例物件,以強制JVM位元組碼的順序性。

相關文章