Java記憶體模型-final域的記憶體語義

來源:互聯網
上載者:User

標籤:lex   print   時間片   記憶體模型   cape   example   就是   stat   adl   

一 引言

  說到final你肯定知道它是Java中的關鍵字,那麼它所在Java中的作用你知道嗎?不知道的話,請前往這篇瞭解下https://www.cnblogs.com/yuanfy008/p/8021673.html

  今天我們來說說final域在JMM中的記憶體語義。

二 final域的重定序

  開門見山,對於final域,編譯器和處理器一定要遵守兩個重定序(JSR-133才增強了final域):

  1)在建構函式內對一個final域的寫入,與隨後把這個被構造對象的引用賦值給一個引用變數,這個兩個操作不能被重排序。

  2)初次讀一個包含final域的對象的引用,與隨後初次讀這個final域,這兩個操作之間不能重排序。

  下面我們通過案例來說明這兩點(假設線程1執行writer(),隨後另一個線程執行reader()方法):

public class FinalExample {    static volatile boolean flag = true;    int i = 0;    final int j;    static FinalExample obj;    public FinalExample() { // 建構函式        i = 1;              // 寫普通域        j = 2;              // 寫final域    }    public static void writer() { // 線程1寫入        obj = new FinalExample();    }    public static void reader() { // 線程2讀取        FinalExample example = obj; // 讀對象引用        System.out.println(example.i); // 讀普通域        System.out.println(example.j); // 讀final域    }}

  寫final域的重定序禁止把final域的寫重排序到建構函式之外。這個規則的實現包含下面兩個方面:

  1)JMM禁止編譯器吧final域的寫重排序到建構函式之外。

  2)編譯器會在final域的寫之後,建構函式return之前,插入一個StoreStore屏障。這個屏障禁止處理器把final域的寫重排序到建構函式之外。

  所以線程1執行順序如(其中寫普通域的順序無法保證,理論上是存在下面三種情況的,要想驗證普通域是否有重排序的結果有點難,因為無法保證線程1把普通域重排序後,線程2能夠讀取它之前的0值):

  讀final域的重定序是:在一個線程中,初次讀這個對象引用與初次讀該對象包含的final域,JMM禁止處理器重排序這兩個操作。其中編譯器會在讀final網域作業的前面插入一個LoadLoad屏障。由於插入了loadLoad屏障,讀普通域i的操作是不會重排序到讀final域,但是不保證它會重排到讀對象引用這個操作的前面。所以線程2的一個執行順序就能想象到了,這裡就不畫線程2的執行順序圖了。 

三 final域為參考型別

  如果finaly域為參考型別,JMM中是怎麼處理的呢?對於參考型別,寫final域的重定序對編譯器和處理器增加了如下約束:在建構函式內對一個final引用的對象的成員域的寫入,與隨後在建構函式外把這個被構造對象的引用賦值給一個引用變數,這兩個操作之間不能重排序。 

       以上及其以上都要注意:只針對於建構函式方法內。另外要想以上規則確保,還需要一個條件:在建構函式內部,不能讓這個被構造對象的引用被其他線程可見,也就是對應引用不能再建構函式中“逸出”。如下案例:

  

class FinalReferenceEscapeExample {    final int i;    static FinalReferenceEscapeExample obj;    public FinalReferenceEscapeExample() {        i = 1;      // 1        obj = this; // 2 this引用逸出    }    public static void writer() { // 線程1        new FinalReferenceEscapeExample();    }    public static void reader() { // 線程2        if (obj != null) {            System.out.println(obj.i);        }    }}

 

  上面程式,第一步寫final域與第二步是不保證重排序的。所以當第一步與第二步重排之後,線程1執行完這步(obj = this)後,時間片分給第二個線程執行,那麼線程2將會擷取final域初始化之前的值,這肯定就違背了程式的初衷。

  

Java記憶體模型-final域的記憶體語義

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.