高并發(fā)編程-happens-before
文章目錄
概述
happens-before定義
happens-before規(guī)則
7個原則
volatile變量規(guī)則
start()規(guī)則
join()規(guī)則
happens-before與JMM的關(guān)系
JMM的設(shè)計
概述
happens-before是JMM最核心的概念,理解happens-before是理解JMM的關(guān)鍵
從JDK 5開始,Java使用新的JSR-133內(nèi)存模型。
JSR-133使用happens-before的概念來闡述操作之間的內(nèi)存可見性。
在JMM中,如果一個操作執(zhí)行的結(jié)果需要對另一個操作可見,那么這兩個操作之間必須要存在happens-before關(guān)系。
這里提到的兩個操作既可以是在一個線程之內(nèi),也可以是在不同線程之間。
happens-before定義
JSR-133使用happens-before的概念來指定兩個操作之間的執(zhí)行順序。由于這兩個操作可以在一個線程之內(nèi),也可以是在不同線程之間。因此,JMM可以通過happens-before關(guān)系向開發(fā)人員提供跨線程的內(nèi)存可見性保證(如果A線程的寫操作a與B線程的讀操作b之間存在happens-before關(guān)系,盡管a操作和b操作在不同的線程中執(zhí)行,但JMM向程序員保證a操作將對b操作可見)。
happens-before規(guī)則
7個原則
程序順序規(guī)則:一個線程中的每個操作,happens-before于該線程中的任意后續(xù)操作
監(jiān)視器鎖規(guī)則:對一個鎖的解鎖,happens-before于隨后對這個鎖的加鎖。
volatile變量規(guī)則:對一個volatile域的寫,happens-before于任意后續(xù)對這個volatile域的讀。
傳遞性:如果A happens-before B,且B happens-before C,那么A happens-before C
start()規(guī)則:如果線程A執(zhí)行操作ThreadB.start()(啟動線程B),那么A線程的ThreadB.start()操作happens-before于線程B中的任意操作。
join()規(guī)則:如果線程A執(zhí)行操作ThreadB.join()并成功返回,那么線程B中的任意操作happens-before于線程A從ThreadB.join()操作成功返回。
volatile變量規(guī)則
volatile寫-讀建立的happens-before關(guān)系圖如下
·1 happens-before 2和3 happens-before 4由程序順序規(guī)則產(chǎn)生。由于編譯器和處理器都要遵守as-if-serial語義,也就是說,as-if-serial語義保證了程序順序規(guī)則。因此,可以把程序順序規(guī)則看成是對as-if-serial語義的“封裝”。
2 happens-before 3是由volatile規(guī)則產(chǎn)生。前面提到過,對一個volatile變量的讀,總是能看到(任意線程)之前對這個volatile變量最后的寫入。因此,volatile的這個特性可以保證實現(xiàn)volatile規(guī)則。
·1 happens-before 4是由傳遞性規(guī)則產(chǎn)生的。這里的傳遞性是由volatile的內(nèi)存屏障插入策略和volatile的編譯器重排序規(guī)則共同來保證的。
start()規(guī)則
假設(shè)線程A在執(zhí)行的過程中,通過執(zhí)行ThreadB.start()來啟動線程B;同時,假設(shè)線程A在執(zhí)行ThreadB.start()之前修改了一些共享變量,線程B在開始執(zhí)行后會讀這些共享變量。下圖是該程序?qū)?yīng)的happens-before關(guān)系圖。
1 happens-before 2由程序順序規(guī)則產(chǎn)生。2 happens-before 4由start()規(guī)則產(chǎn)生。根據(jù)傳遞性,將有1 happens-before 4。這實意味著,線程A在執(zhí)行ThreadB.start()之前對共享變量所做的修改,接下來在線程B開始執(zhí)行后都將確保對線程B可見。
join()規(guī)則
假設(shè)線程A在執(zhí)行的過程中,通過執(zhí)行ThreadB.join()來等待線程B終止;同時,假設(shè)線程B在終止之前修改了一些共享變量,線程A從ThreadB.join()返回后會讀這些共享變量。 該程序?qū)?yīng)的happens-before關(guān)系圖如下:
2 happens-before 4由join()規(guī)則產(chǎn)生;4 happens-before 5由程序順序規(guī)則產(chǎn)生。根據(jù)傳遞性規(guī)則,將有2 happens-before 5。這意味著,線程A執(zhí)行操作ThreadB.join()并成功返回后,線程B中的任意操作都將對線程A可見。
兩個操作之間具有happens-before關(guān)系,并不意味著前一個操作必須要在后一個操作之前執(zhí)行!happens-before僅僅要求前一個操作(執(zhí)行的結(jié)果)對后一個操作可見,且前一個操作按順序排在第二個操作之前(the first is visible to and ordered before the second)
happens-before與JMM的關(guān)系
如上圖所示,一個 happens-before 規(guī)則通常對應(yīng)于多個編譯器重排序規(guī)則和處理器重排序規(guī)則。對于開發(fā)人員來說,happens-before 規(guī)則簡單易懂,它避免程序員為了理解 JMM 提供的內(nèi)存可見性保證而去學(xué)習(xí)復(fù)雜的重排序規(guī)則以及這些規(guī)則的具體實現(xiàn)
JMM的設(shè)計
從JMM設(shè)計者的角度,在設(shè)計JMM時,需要考慮兩個關(guān)鍵因素
開發(fā)人員對內(nèi)存模型的使用。程序員希望內(nèi)存模型易于理解、易于編程。
開發(fā)人員希望基于一個強內(nèi)存模型來編寫代碼。
編譯器和處理器對內(nèi)存模型的實現(xiàn)。編譯器和處理器希望內(nèi)存模型對它們的束縛越少越好,這樣它們就可以做盡可能多的優(yōu)化來提高性能。
編譯器和處理器希望實現(xiàn)一個弱內(nèi)存模型。
由于這兩個因素互相矛盾,所以JSR-133設(shè)計JMM時的核心目標(biāo)就是找到一個好的
平衡點:一方面,要為程序員提供足夠強的內(nèi)存可見性保證;另一方面,對編譯器和處理器的限制要盡可能地放松 。
舉個例子來看JSR-133是如何實現(xiàn)這一目標(biāo)的
double pi = 3.14; // A double r = 1.0; // B double area = pi * r * r; // C
1
2
3
上面計算圓的面積的示例代碼存在3個happens-before關(guān)系,如下。
·A happens-before B。 ·B happens-before C。 ·A happens-before C。
1
2
3
在3個happens-before關(guān)系中,2和3是必需的,但1是不必要的。因此,JMM把happens-before要求禁止的重排序分為了下面兩類
·
會改變程序執(zhí)行結(jié)果的重排序。
不會改變程序執(zhí)行結(jié)果的重排序。
JMM對這兩種不同性質(zhì)的重排序,采取了不同的策略,如下
對于會改變程序執(zhí)行結(jié)果的重排序,JMM要求編譯器和處理器必須禁止這種重排序
對于不會改變程序執(zhí)行結(jié)果的重排序,JMM對編譯器和處理器不做要求(JMM允許這種重排序)。
從上圖可以看出兩點,如下
JMM向程序員提供的happens-before規(guī)則能滿足程序員的需求。
JMM的happens-before規(guī)則不但簡單易懂,而且也向開發(fā)人員提供了足夠強的內(nèi)存可見性保證(有些內(nèi)存可見性保證其并不一定真實存在,比如上面的A happens-before B)
·JMM對編譯器和處理器的束縛已經(jīng)盡可能少。從上面的分析可以看出,JMM其實是在遵循一個基本原則:
只要不改變程序的執(zhí)行結(jié)果(指的是單線程程序和正確同步的多線程程序)編譯器和處理器怎么優(yōu)化都行。
Java 任務(wù)調(diào)度
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。