首先是字節碼文件生成。這裏注意,在調用javap 查看字節碼指令的時候,要指定-verbose參數,否則查看到信息不徹底,只有類的基礎信息。 java
一段以下的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);
}
}
}
複製代碼
編譯後,以下:bash
看圖中 IfP()方法的字節碼部分。if_icmple
複製代碼
就是if語句的比較部分,緊接着下面的幾條指令,異步
new invokespecial
複製代碼
兩條指令,this
new
複製代碼
是用來spa
建立一個對象, 並將其引用引用值壓入棧頂線程
invokespecial #4
複製代碼
是用來code
用於調用一些須要特殊處理的實例方法,包括實例初始化方法、 私有方法和父類方法。 #4處就是超類實例方法跟類實例方法, 後面的部分還有指令是用來 將對象引用賦值給變量的 因此,這裏能夠看出,一條 TestP p = new Testp(1);cdn
有3部分指令共同完成。那JVM保證的是最終結果的正確性,並不會保證字節碼的運行順序,也就是說,頗有可能不是按上述順序運行字節碼指令的,那如此的話,假如實例化初始方法的調用晚於synchronized關鍵字後,那就會形成異步的另外一條線程在當前線程初始化單例後仍然進入第二個if 判null 的語法塊內的狀況,這樣就錯了。對象
因此,雙重校驗的單例模式,必定要用volitile修飾單例對象,以強制JVM字節碼的順序性。blog