JSR133給Java內存模型定義的happen-before規則

  1. 單線程規則:同一個線程中的每一個操做都happens-before於出如今其後的任何一個操做。app

  2. 對一個監視器的解鎖操做happens-before於每個後續對同一個監視器的加鎖操做。函數

  3. 對volatile字段的寫入操做happens-before於每個後續的對同一個volatile字段的讀操做。線程

  4. Thread.start()的調用操做會happens-before於啓動線程裏面的操做。code

  5. 一個線程中的全部操做都happens-before於其餘線程成功返回在該線程上的join()調用後的全部操做。對象

  6. 一個對象構造函數的結束操做happens-before與該對象的finalizer的開始操做。內存

  7. 傳遞性規則:若是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.

相關文章
相關標籤/搜索