您好,登錄后才能下訂單哦!
這篇文章主要介紹“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í)吧!
Java內(nèi)存模型是共享內(nèi)存的并發(fā)模型,線程之間主要通過(guò)讀-寫(xiě)共享變量來(lái)完成隱式通信。
Java中的共享變量是存儲(chǔ)在內(nèi)存中的,多個(gè)線程由其工作內(nèi)存,其工作方式是將共享內(nèi)存中的變量拿出來(lái)放在工作內(nèi)存,操作完成后,再將最新的變量放回共享變量,這時(shí)其他的線程就可以獲取到最新的共享變量。
從橫向去看看,線程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)禁止某些特殊的處理器重排序。
(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í)行的。
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)行重排序)
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允許這種重排序)。
(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)始。
利用程序順序規(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í)行順序。
不管怎么重排序(編譯器和處理器為了提高并行度),單線程程序的執(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í)用的文章!
免責(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)容。