單線程規則:同一個線程中的每一個操做都happens-before於出如今其後的任何一個操做。app
對一個監視器的解鎖操做happens-before於每個後續對同一個監視器的加鎖操做。函數
對volatile字段的寫入操做happens-before於每個後續的對同一個volatile字段的讀操做。線程
Thread.start()的調用操做會happens-before於啓動線程裏面的操做。code
一個線程中的全部操做都happens-before於其餘線程成功返回在該線程上的join()調用後的全部操做。對象
一個對象構造函數的結束操做happens-before與該對象的finalizer的開始操做。內存
傳遞性規則:若是A操做happens-before於B操做,而B操做happens-before與C操做,那麼A動做happens-before於C操做。同步
實際上這組happens-before規則定義了操做之間的內存可見性,若是A操做happens-before B操做,那麼A操做的執行結果(好比對變量的寫入)一定在執行B操做時可見。class
爲了更加深刻的瞭解這些happens-before規則,咱們來看一個例子:變量
//線程A,B共同訪問的代碼 Object lock = new Object(); int a=0; int b=0; int c=0; //線程A,調用以下代碼 synchronized(lock){ a=1; //1 b=2; //2 } //3 c=3; //4 //線程B,調用以下代碼 synchronized(lock){ //5 System.out.println(a); //6 System.out.println(b); //7 System.out.println(c); //8 }
咱們假設線程A先運行,分別給a,b,c三個變量進行賦值(注:變量a,b的賦值是在同步語句塊中進行的),而後線程B再運行,分別讀取出這三個變量的值並打印出來。那麼線程B打印出來的變量a,b,c的值分別是多少?構造函數
根據單線程規則,在A線程的執行中,咱們能夠得出1操做happens before於2操做,2操做happens before於3操做,3操做happens before於4操做。同理,在B線程的執行中,5操做happens before於6操做,6操做happens before於7操做,7操做happens before於8操做。而根據監視器的解鎖和加鎖原則,3操做(解鎖操做)是happens before 5操做的(加鎖操做),再根據傳遞性 規則咱們能夠得出,操做1,2是happens before 操做6,7,8的。
則根據happens-before的內存語義,操做1,2的執行結果對於操做6,7,8是可見的,那麼線程B裏,打印的a,b確定是1和2. 而對於變量c的操做4,和操做8. 咱們並不能根據現有的happens before規則推出操做4 happens before於操做8. 因此在線程B中,訪問的到c變量有可能仍是0,而不是3.