JMM之重排序

重排序定義

在前面我們提到過(guò),重排序是編譯器和處理器為了優(yōu)化程序性能而對(duì)指令序列重新排序的一種手段。但是我們也知道代碼不可能毫無(wú)原則的進(jìn)行重排序,如果是毫無(wú)原則的進(jìn)行重排序,那么我們的代碼將無(wú)法獲得預(yù)期的結(jié)果。因此重排序必須滿(mǎn)足如下原則:

  1. 在單線(xiàn)程中不改變運(yùn)行結(jié)果
  2. 操作不具備數(shù)據(jù)依賴(lài)性

那這兩條原則如何理解么,我們先來(lái)看看下面的定義

數(shù)據(jù)依賴(lài)性

數(shù)據(jù)依賴(lài)性的意思是,若果兩個(gè)操作訪(fǎng)問(wèn)同一個(gè)變量,并且其中一個(gè)操作是寫(xiě)操作,那么這兩個(gè)操作就具備數(shù)據(jù)依賴(lài)性。

as-if-serial語(yǔ)義

as-if-serial語(yǔ)義的意思是,不管如何重排序,在單線(xiàn)程中,程序的執(zhí)行結(jié)果不能被改變。編譯器、處理器和runtime都不行遵守as-if-serial語(yǔ)義。注意:as-if-serial只對(duì)單線(xiàn)程有效,對(duì)多線(xiàn)程無(wú)效。

了解了數(shù)據(jù)依賴(lài)性和as-if-serial后,我們看看如下示例:

int a = 1;      // A
int b = 2;      // B 
int c = a * b;  // C

在上述示例中,我們根據(jù)數(shù)據(jù)依賴(lài)性可以分析出,A和C具備數(shù)據(jù)依賴(lài)性,B和C具備依數(shù)據(jù)賴(lài)性,A和B之前不存在數(shù)據(jù)依賴(lài)性關(guān)系,因此編譯器和處理器在進(jìn)行重排序時(shí),A和B可以進(jìn)行重排序,但是A和C,以及B和C是不能進(jìn)行重排序的。因?yàn)楸M管A和B進(jìn)行了重排序,但是他們不影響程序在單線(xiàn)程中運(yùn)行的結(jié)果。

在這里,我們聯(lián)想前面講到過(guò)的happens-before,可以看出上面代碼存在3個(gè)happens-before關(guān)系。它們分別是:

  1. A happens-before B
  2. B happens-before C
  3. A happens-before C

在這里L(fēng)Z再次強(qiáng)調(diào),happens-before與時(shí)間上的先后完全沒(méi)有關(guān)系,happens-before僅僅要求,前一個(gè)操作的結(jié)果對(duì)后一個(gè)操作可見(jiàn)。在這里操作A的結(jié)果不需要對(duì)B可見(jiàn),重排序操作A和操作B的結(jié)果與按照A happens-before B 的執(zhí)行結(jié)果一致。在這種情況下JMM認(rèn)為這種重排序并不非法,JMM允許這種重排序。

重排序?qū)Χ嗑€(xiàn)程的影響

在了解了重排序的概率和數(shù)據(jù)依賴(lài)性以及as-if-serial的定以后,我們知道重排序在單線(xiàn)程中是沒(méi)有影響的,那么重排序?qū)Χ嗑€(xiàn)程是否有影響呢?我么首先來(lái)看看下面的代碼示例:

class ReorderExample {
    int a = 0;
    boolean flag = false;

    public void writer() {
        a = 1; // 1
        flag = true; // 2
    } 
    public void reader() {
        if (flag) { // 3
            int i = a * a; // 4
        }
    }
}

假設(shè)現(xiàn)在有A和B兩個(gè)下次,此時(shí)A線(xiàn)程執(zhí)行write()方法,B線(xiàn)程執(zhí)行reader()方法,那么在線(xiàn)程B在執(zhí)行操作4時(shí),能否獲取到a=1呢?答案是不一定。

由于操作1 和操作2沒(méi)有數(shù)據(jù)依賴(lài)性,因此編譯器和處理器能夠?qū)@2個(gè)操作進(jìn)行重排序,當(dāng)操作1和操作2進(jìn)行重排序時(shí),此時(shí)程序的執(zhí)行時(shí)序圖是這樣子的

重排序時(shí)序

如上圖所示,操作1和操作2進(jìn)行了重排序。程序在執(zhí)行時(shí),線(xiàn)程A首先寫(xiě)標(biāo)記變量flag,隨后線(xiàn)程B讀取這變量,由于條件判斷為真,線(xiàn)程B將讀取變量a,此時(shí)變量a還沒(méi)有被先A寫(xiě)入,在這里多線(xiàn)程程序的語(yǔ)義被重排序破壞掉了。

同樣,操作3和操作4也可以進(jìn)行重排序,但是操作3和操作4存在控制依賴(lài)的關(guān)系,即當(dāng)操作3為真時(shí),操作4才可以執(zhí)行。當(dāng)代碼中存在控制依賴(lài)關(guān)系時(shí),會(huì)影響指令序列執(zhí)行的并行度。為此,編譯器和處理器采用猜測(cè)執(zhí)行來(lái)克服控制相關(guān)性對(duì)并行度的影響。以處理器的猜測(cè)執(zhí)行為例,執(zhí)行線(xiàn)程B的處理器可以提前計(jì)算a*a,然后把計(jì)算的結(jié)果保存到重排序緩存中,當(dāng)操作3的結(jié)果為真時(shí),就把改計(jì)算結(jié)果寫(xiě)入變量i中。

通過(guò)上面的分析,我可以得出結(jié)論:重排序?qū)尉€(xiàn)程的執(zhí)行結(jié)果沒(méi)有影響,但是在多線(xiàn)程中,重排序可能會(huì)改變程序執(zhí)行的結(jié)果。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1.什么是數(shù)據(jù)依賴(lài)性? 如果兩個(gè)操作訪(fǎng)問(wèn)同一個(gè)變量,且有一個(gè)是寫(xiě)操作,此時(shí)兩個(gè)操作就存在數(shù)據(jù)依賴(lài)性。 編譯器和處理...
    是一動(dòng)不動(dòng)的friend閱讀 439評(píng)論 0 0
  • 并發(fā)編程中,需要處理兩個(gè)關(guān)鍵問(wèn)題: 線(xiàn)程之間如何通信?—共享內(nèi)存+消息傳遞 線(xiàn)程之間如何同步? java的并發(fā)采用...
    AlanKim閱讀 477評(píng)論 0 4
  • 重排序是指編譯器和處理器為了優(yōu)化程序性能而對(duì)指令序列進(jìn)行重新排序的一種手段 在程序執(zhí)行時(shí),為了提高性能,編譯器和處...
    T_log閱讀 448評(píng)論 0 0
  • Java的并發(fā)采用的是共享內(nèi)存模型(而非消息傳遞模型),線(xiàn)程之間共享程序的公共狀態(tài),線(xiàn)程之間通過(guò)寫(xiě)-讀內(nèi)存中的公共...
    阿斯蒂芬2閱讀 603評(píng)論 0 1
  • 第2章 java并發(fā)機(jī)制的底層實(shí)現(xiàn)原理 Java中所使用的并發(fā)機(jī)制依賴(lài)于JVM的實(shí)現(xiàn)和CPU的指令。 2.1 vo...
    kennethan閱讀 1,541評(píng)論 0 2

友情鏈接更多精彩內(nèi)容