国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 學(xué)院 > 開(kāi)發(fā)設(shè)計(jì) > 正文

深入理解Javafinal變量的內(nèi)存模型

2019-11-14 15:12:18
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

對(duì)于 final 域,編譯器和處理器要遵守兩個(gè)重排序規(guī)則:

  • 在構(gòu)造函數(shù)內(nèi)對(duì)一個(gè) final 域的寫(xiě),與隨后把這個(gè)構(gòu)造對(duì)象的引用賦值給一個(gè)變量,這兩個(gè)操作之間不能重排序
  • 初次讀一個(gè)包含 final 域的對(duì)象的引用,與隨后初次讀這個(gè) final 域,這兩個(gè)操作之間不能重排序

舉個(gè)例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class FinAlexample {
    int i;// 普通變量
    final int j;// final 變量
    static FinalExample obj;
 
    public FinalExample() {
        i = 1;// 寫(xiě)普通域
        j = 2;// 寫(xiě) final 域
    }
 
    public static void writer() {// 寫(xiě)線(xiàn)程 A 執(zhí)行
        obj = new FinalExample();
    }
 
    public static void reader() {// 讀線(xiàn)程 B 執(zhí)行
        FinalExample object = obj;
        int a = object.i;
        int b = object.j;
    }
}

這里假設(shè)一個(gè)線(xiàn)程 A 執(zhí)行 writer ()方法,隨后另一個(gè)線(xiàn)程 B 執(zhí)行 reader ()方法。

寫(xiě) final 域的重排序規(guī)則

在寫(xiě) final 域的時(shí)候有兩個(gè)規(guī)則:

  • JMM 禁止編譯器把 final 域的寫(xiě)重排序到構(gòu)造函數(shù)之外
  • 編譯器會(huì)在 final 域的寫(xiě)之后,構(gòu)造函數(shù) return 之前,插入一個(gè) StoreStore 屏障,這個(gè)屏障禁止處理器把 final 域的寫(xiě)重排序到構(gòu)造函數(shù)之外。

分析上面的代碼。

write 方法,只包含一行 obj = new FinalExample();,但是包含兩個(gè)步驟:

  • 構(gòu)造一個(gè) FinalExample 對(duì)象
  • 把對(duì)象的引用賦值給 obj

假設(shè)線(xiàn)程 B 當(dāng)中讀 obj 與讀成員域之間沒(méi)有重排序。那么執(zhí)行時(shí)序可能如下:

寫(xiě) final 域的重排序規(guī)則可以確保:在對(duì)象引用為任意線(xiàn)程可見(jiàn)之前,對(duì)象的 final 域已經(jīng)被正確初始化過(guò)了,而普通域不具有這個(gè)保障。

讀 final 域的重排序規(guī)則

讀 final 域的重排序規(guī)則如下:

  • 在一個(gè)線(xiàn)程中,初次讀對(duì)象引用與初次讀該對(duì)象包含的 final 域,JMM 禁止處理器重排序這兩個(gè)操作(注意,這個(gè)規(guī)則僅僅針對(duì)處理器)。編譯器會(huì)在讀 final 域操作的前面插入一個(gè) LoadLoad 屏障。

reader() 方法包含三個(gè)操作:

  1. 初次讀引用變量 obj;
  2. 初次讀引用變量 obj 指向?qū)ο蟮钠胀ㄓ?j。
  3. 初次讀引用變量 obj 指向?qū)ο蟮?final 域 i。

現(xiàn)在我們假設(shè)寫(xiě)線(xiàn)程 A 沒(méi)有發(fā)生任何重排序,那么執(zhí)行時(shí)序可能是:

上面的圖可以看到對(duì)普通變量 i 的讀取重排序到了讀對(duì)象引用之前,在讀普通域時(shí)候,該域還沒(méi)被寫(xiě)線(xiàn)程 A 寫(xiě)入,這是一個(gè)錯(cuò)誤的讀取操作。而讀 final 域已經(jīng)被 A 線(xiàn)程初始化了,這個(gè)讀取操作是正確的。

讀 final 域的重排序規(guī)則可以確保:在讀一個(gè)對(duì)象的 final 域之前,一定會(huì)先讀包含 這個(gè) final 域的對(duì)象的引用。在這個(gè)示例程序中,如果該引用不為 null,那么引用 對(duì)象的 final 域一定已經(jīng)被 A 線(xiàn)程初始化過(guò)了。

如果 final 域是引用類(lèi)型

如果 final 域是引用類(lèi)型,寫(xiě) final 域的重排序規(guī)則對(duì)編譯器和處理器增加了如下約束:

  • 在構(gòu)造函數(shù)內(nèi)對(duì)一個(gè) final 引用的對(duì)象的成員域的寫(xiě)入,與隨后在構(gòu)造函數(shù)外把這個(gè)被構(gòu)造對(duì)象的引用賦值給一個(gè)引用變量,這兩個(gè)操作之間不能重排序。

如下代碼例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class FinalReferenceExample {
    final int[] intArray;
    static FinalReferenceExample obj;
 
    public FinalReferenceExample() {
        intArray = new int[1];// 1
        intArray[0] = 1;// 2
    }
 
    public static void writerOne() {// A線(xiàn)程執(zhí)行
        obj = new FinalReferenceExample(); // 3
    }
 
    public static void reader() {// 寫(xiě)線(xiàn)程 B 執(zhí)行
        if (obj != null) { // 4
            int temp1 = obj.intArray[0]; // 5
        }
    }
}

假設(shè)首先線(xiàn)程 A 執(zhí)行 writerOne()方法,執(zhí)行完后線(xiàn)程 B 執(zhí)行reader 方法,JMM 可以確保讀線(xiàn)程 B 至少能看到寫(xiě)線(xiàn)程 A 在構(gòu)造函數(shù)中對(duì) final 引用對(duì)象的成員域的寫(xiě)入。

避免對(duì)象引用在構(gòu)造函數(shù)當(dāng)中溢出

代碼如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class FinalReferenceEscapeExample {
    final int i;
    static FinalReferenceEscapeExample obj;
 
    public FinalReferenceEscapeExample() {
        i = 1;// 1
        obj = this;// 2 避免怎么做!!!
    }
 
    public static void writer() {
        new FinalReferenceEscapeExample();
    }
 
    public static void reader() {
        if (obj != null) {// 3
            int temp = obj.i; // 4
        }
    }
}

假設(shè)一個(gè)線(xiàn)程 A 執(zhí)行 writer()方法,另一個(gè)線(xiàn)程 B 執(zhí)行 reader()方法。

這里的操作 2 使得對(duì)象還未完成構(gòu)造前就為線(xiàn)程 B 可見(jiàn)。即使這里的操作 2 是構(gòu)造函數(shù)的最后 一步,且即使在程序中操作 2 排在操作 1 后面,執(zhí)行 read()方法的線(xiàn)程仍然可能無(wú) 法看到 final 域被初始化后的值,因?yàn)檫@里的操作 1 和操作 2 之間可能被重排序。

在構(gòu)造函數(shù)返回前,被構(gòu)造對(duì)象的引用不能為其他線(xiàn)程可 見(jiàn),因?yàn)榇藭r(shí)的 final 域可能還沒(méi)有被初始化。在構(gòu)造函數(shù)返回后,任意線(xiàn)程都將 保證能看到 final 域正確初始化之后的值。

 

全能程序員交流QQ群290551701,群內(nèi)程序員都是來(lái)自,百度、阿里、京東、小米、去哪兒、餓了嗎、藍(lán)港等高級(jí)程序員 ,擁有豐富的經(jīng)驗(yàn)。加入我們,直線(xiàn)溝通技術(shù)大牛,最佳的學(xué)習(xí)環(huán)境,了解業(yè)內(nèi)的一手的資訊。如果你想結(jié)實(shí)大牛,那 就加入進(jìn)來(lái),讓大牛帶你超神!


發(fā)表評(píng)論 共有條評(píng)論
用戶(hù)名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 云浮市| 长垣县| 曲阳县| 茂名市| 西乌珠穆沁旗| 巴塘县| 沁源县| 遵义市| 民县| 汉沽区| 合山市| 信阳市| 湘阴县| 称多县| 岑巩县| 万载县| 融水| 正蓝旗| 霍林郭勒市| 朝阳县| 丘北县| 依安县| 紫阳县| 沧州市| 志丹县| 江西省| 普兰县| 沙雅县| 泰顺县| 凤凰县| 鞍山市| 和顺县| 天台县| 涟源市| 双城市| 聂拉木县| 平昌县| 尚义县| 板桥市| 丰镇市| 阿瓦提县|