溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Happens-Before原則和As-If-Serial語(yǔ)義是什么

發(fā)布時(shí)間:2021-10-12 16:18:06 來(lái)源:億速云 閱讀:142 作者:iii 欄目:編程語(yǔ)言

這篇文章主要介紹“Happens-Before原則和As-If-Serial語(yǔ)義是什么”,在日常操作中,相信很多人在Happens-Before原則和As-If-Serial語(yǔ)義是什么問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”Happens-Before原則和As-If-Serial語(yǔ)義是什么”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!

JMM內(nèi)存模型

Java內(nèi)存模型是共享內(nèi)存的并發(fā)模型線程之間主要通過(guò)讀-寫(xiě)共享變量來(lái)完成隱式通信。

Java中的共享變量是存儲(chǔ)在內(nèi)存中的,多個(gè)線程由其工作內(nèi)存,其工作方式是將共享內(nèi)存中的變量拿出來(lái)放在工作內(nèi)存,操作完成后,再將最新的變量放回共享變量,這時(shí)其他的線程就可以獲取到最新的共享變量。

Happens-Before原則和As-If-Serial語(yǔ)義是什么


從橫向去看看,線程A和線程B就好像通過(guò)共享變量在進(jìn)行隱式通信。這其中有很有意思的問(wèn)題,如果線程A更新后數(shù)據(jù)并沒(méi)有及時(shí)寫(xiě)回到主存,而此時(shí)線程B讀到的是過(guò)期的數(shù)據(jù),這就出現(xiàn)了 “臟讀” 現(xiàn)象。


     為避免臟讀,可以通過(guò)同步機(jī)制(控制不同線程間操作發(fā)生的相對(duì)順序)來(lái)解決或者通過(guò)volatile關(guān)鍵字使得每次volatile變量都能夠強(qiáng)制刷新到主存,從而對(duì)每個(gè)線程都是可見(jiàn)的。

重排序

執(zhí)行程序時(shí),為了提高性能,編譯器和處理器常常會(huì)對(duì)指令進(jìn)行重排序。一般重排序可以分為如下三種:如圖,1.屬于編譯器重排序,而2和3統(tǒng)稱(chēng)為處理器重排序。


這些重排序會(huì)導(dǎo)致線程安全的問(wèn)題,一個(gè)很經(jīng)典的例子就是DCL問(wèn)題。JMM的編譯器重排序規(guī)則會(huì)禁止一些特定類(lèi)型的編譯器重排序;針對(duì)處理器重排序,編譯器在生成指令序列的時(shí)候會(huì)通過(guò)插入內(nèi)存屏障指令來(lái)禁止某些特殊的處理器重排序。

Happens-Before原則和As-If-Serial語(yǔ)義是什么

  • (1)編譯器優(yōu)化的重排序:編譯器在不改變單線程程序語(yǔ)義的前提下,可以重新安排語(yǔ)句的執(zhí)行順序;

  • (2)指令級(jí)并行的重排序:現(xiàn)代處理器采用指令級(jí)并行技術(shù)來(lái)將多條指令重疊執(zhí)行。如果不存在數(shù)據(jù)依賴性,處理器可以改變語(yǔ)句對(duì)應(yīng)機(jī)器指令的執(zhí)行順序;

  • (3)內(nèi)存系統(tǒng)的重排序。由于處理器使用緩存和讀/寫(xiě)緩沖區(qū),這使得加載和存儲(chǔ)操作看上去可能是在亂序執(zhí)行的。


舉個(gè)例子:

	public double rectangleArea(double length , double width){
		double leng;
		double wid;
		leng=length;//A
		wid=width;//B
		double area=leng*wid;//C
		return area;
	}

由于A,B之間沒(méi)有任何關(guān)系,對(duì)最終結(jié)果也不會(huì)存在關(guān)系,它們之間執(zhí)行順序可以重排序。因此可以執(zhí)行順序可以是A->B->C或者B->A->C執(zhí)行最終結(jié)果都是3.14,即A和B之間沒(méi)有數(shù)據(jù)依賴性。

因?yàn)锳  happens-before  B,所以A操作產(chǎn)生的結(jié)果leng一定要對(duì)B操作可見(jiàn),但是現(xiàn)在B操作并沒(méi)有用到length,所以這兩個(gè)操作可以重排序,那A操作是否可以和C操作重排序呢,如果A操作和C操作進(jìn)行了重排序,因?yàn)閘eng沒(méi)有被賦值,所以leng=0,area=0*wid也就是area=0;這個(gè)結(jié)果顯然是錯(cuò)誤的,所以A操作是不能和C操作進(jìn)行重排序的(這就是注2中說(shuō)的前一個(gè)操作的執(zhí)行結(jié)果必須對(duì)后羿操作可見(jiàn),如果不滿足這個(gè)要求就不允許這兩個(gè)操作進(jìn)行重排序)

什么是happens-before(面向JMM的規(guī)范原則和編譯器層面原則) 多線程場(chǎng)景下

JMM可以通過(guò)happens-before關(guān)系提供跨線程的內(nèi)存可見(jiàn)性保證如果A線程的寫(xiě)操作a與B線程的讀操作b之間存在happens-before關(guān)系,盡管a操作和b操作在不同的線程中執(zhí)行,但JMM向程序員保證a操作將對(duì)b操作可見(jiàn))。

具體的定義:

  • 1)如果一個(gè)操作happens-before另一個(gè)操作,那么第一個(gè)操作的執(zhí)行結(jié)果將對(duì)第二個(gè)操作可見(jiàn),而且第一個(gè)操作的執(zhí)行順序排在第二個(gè)操作之前。

  • 2)兩個(gè)操作之間存在happens-before關(guān)系,并不意味著Java平臺(tái)的具體實(shí)現(xiàn)必須要按照happens-before關(guān)系指定的順序來(lái)執(zhí)行。如果重排序之后的執(zhí)行結(jié)果,與按happens-before關(guān)系來(lái)執(zhí)行的結(jié)果一致,那么這種重排序并不非法(JMM允許這種重排序)。

具體的規(guī)則:

  • (1) 程序順序規(guī)則:線程中每個(gè)操作,happens-before該線程任意后續(xù)操作。

  • (2) 監(jiān)視器鎖規(guī)則:對(duì)鎖的解鎖,happens-before于隨后對(duì)這個(gè)鎖的加鎖

  • (3) volatile變量規(guī)則:對(duì)volatile域的寫(xiě),happens-before于任意后續(xù)對(duì)這個(gè)volatile域的讀/寫(xiě)。

// 對(duì)一個(gè)volatile變量的寫(xiě)操作happen-before對(duì)此變量的任意操作:
volatile int a;
a = 1; //1
b = a;  //2
//如果線程1 執(zhí)行1,“線程2”執(zhí)行了2,并且“線程1”執(zhí)行后,“線程2”再執(zhí)行,那么符合“volatile的
//happen-before原則”所以“線程2”中的a值一定是1。
  • (4) 傳遞性:如果A happens-before B,且B happens-before C,那么A happens-before C

  • (5) start()規(guī)則如果線程A執(zhí)行操作ThreadB.start()啟動(dòng)線程B),那么A線程的 ThreadB.start() 操作happens-before于線程B中的任意操作。

  • (6) Join()規(guī)則:如果線程A執(zhí)行操作ThreadB.join()并成功返回,那么線程B中的任意操作happens-before于線程A從**ThreadB.join()**操作成功返回。

  • (7) 程序中斷規(guī)則:對(duì)線程interrupted()方法的調(diào)用先行于被中斷線程的代碼檢測(cè)到中斷時(shí)間的發(fā)生

  • (8) 對(duì)象finalize規(guī)則:一個(gè)對(duì)象的初始化完成(構(gòu)造函數(shù)執(zhí)行結(jié)束)先行于發(fā)生它的finalize()方法的開(kāi)始。

happens-before具有傳遞規(guī)則

利用程序順序規(guī)則(規(guī)則1)存在三個(gè)happens-before關(guān)系:

  • A happens-before B

  • B happens-before C

  • A happens-before C

這里的第三個(gè)關(guān)系是利用傳遞性進(jìn)行推論的。這里的第三個(gè)關(guān)系是利用傳遞性進(jìn)行推論的

volatile int var;
int b;
int c;
b = 4; //1
var = 3; //2
c = var; //3
c = b; //4

假設(shè)“線程1”執(zhí)行//1 //2這段代碼,“線程2”執(zhí)行//3 //4這段代碼。如果某次的執(zhí)行順序如下:

//1 //2 //3 //4。那么有如下推導(dǎo)( hd(a,b)表示a happen-before b):

  • 因?yàn)橛衕d(//1,//2) 、hd(//3,//4) (單線程的happen-before原則)

  • 且hd(//2,//3) (volatile的happen-before原則)

  • 所以有 hd(//1,//3),可導(dǎo)出hd(//1,//4) (happen-before原則的傳遞性)

  • 所以變量c的值最后為4 如果某次的執(zhí)行順序如下:

//1 //3 //2// //4 那么最后4的結(jié)果就不能確定嘍。其原因是 //3 //2 直接符合上述八大原則中的任何一個(gè),不能通過(guò)傳遞性推測(cè)出來(lái)什么。


A happens-before B,定義1要求A執(zhí)行結(jié)果對(duì)B可見(jiàn),并且A操作的執(zhí)行順序在B操作之前,但與此同時(shí)利用定義中的第二條,A,B操作彼此不存在數(shù)據(jù)依賴性,兩個(gè)操作的執(zhí)行順序?qū)ψ罱K結(jié)果都不會(huì)產(chǎn)生影響,在不改變最終結(jié)果的前提下,允許A,B兩個(gè)操作重排序,即happens-before關(guān)系并不代表了最終的執(zhí)行順序。

As-If-Serial語(yǔ)義的介紹(面向處理器的語(yǔ)義規(guī)范)單線程場(chǎng)景下

不管怎么重排序(編譯器和處理器為了提高并行度),單線程程序的執(zhí)行結(jié)果不能被改變。編譯器,runtime 和處理器都必須遵守as-if-serial語(yǔ)義。

為了遵守as-if-serial語(yǔ)義,編譯器和處理器不會(huì)對(duì)存在數(shù)據(jù)依賴關(guān)系的操作做重排序,因?yàn)檫@種重排序會(huì)改變執(zhí)行結(jié)果。但是,如果操作之間不存在數(shù)據(jù)依賴關(guān)系,這些操作可能被編譯器和處理器重排序。

到此,關(guān)于“Happens-Before原則和As-If-Serial語(yǔ)義是什么”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI