您好,登錄后才能下訂單哦!
這篇文章主要介紹“Happens-Before原則和As-If-Serial語義是什么”,在日常操作中,相信很多人在Happens-Before原則和As-If-Serial語義是什么問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Happens-Before原則和As-If-Serial語義是什么”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
Java內存模型是共享內存的并發模型,線程之間主要通過讀-寫共享變量來完成隱式通信。
Java中的共享變量是存儲在內存中的,多個線程由其工作內存,其工作方式是將共享內存中的變量拿出來放在工作內存,操作完成后,再將最新的變量放回共享變量,這時其他的線程就可以獲取到最新的共享變量。
從橫向去看看,線程A和線程B就好像通過共享變量在進行隱式通信。這其中有很有意思的問題,如果線程A更新后數據并沒有及時寫回到主存,而此時線程B讀到的是過期的數據,這就出現了 “臟讀” 現象。
為避免臟讀,可以通過同步機制(控制不同線程間操作發生的相對順序)來解決或者通過volatile關鍵字使得每次volatile變量都能夠強制刷新到主存,從而對每個線程都是可見的。
執行程序時,為了提高性能,編譯器和處理器常常會對指令進行重排序。一般重排序可以分為如下三種:如圖,1.屬于編譯器重排序,而2和3統稱為處理器重排序。
這些重排序會導致線程安全的問題,一個很經典的例子就是DCL問題。JMM的編譯器重排序規則會禁止一些特定類型的編譯器重排序;針對處理器重排序,編譯器在生成指令序列的時候會通過插入內存屏障指令來禁止某些特殊的處理器重排序。
(1)編譯器優化的重排序:編譯器在不改變單線程程序語義的前提下,可以重新安排語句的執行順序;
(2)指令級并行的重排序:現代處理器采用指令級并行技術來將多條指令重疊執行。如果不存在數據依賴性,處理器可以改變語句對應機器指令的執行順序;
(3)內存系統的重排序。由于處理器使用緩存和讀/寫緩沖區,這使得加載和存儲操作看上去可能是在亂序執行的。
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之間沒有任何關系,對最終結果也不會存在關系,它們之間執行順序可以重排序。因此可以執行順序可以是A->B->C或者B->A->C執行最終結果都是3.14,即A和B之間沒有數據依賴性。
因為A happens-before B,所以A操作產生的結果leng一定要對B操作可見,但是現在B操作并沒有用到length,所以這兩個操作可以重排序,那A操作是否可以和C操作重排序呢,如果A操作和C操作進行了重排序,因為leng沒有被賦值,所以leng=0,area=0*wid也就是area=0;這個結果顯然是錯誤的,所以A操作是不能和C操作進行重排序的(這就是注2中說的前一個操作的執行結果必須對后羿操作可見,如果不滿足這個要求就不允許這兩個操作進行重排序)
JMM可以通過happens-before關系提供跨線程的內存可見性保證(如果A線程的寫操作a與B線程的讀操作b之間存在happens-before關系,盡管a操作和b操作在不同的線程中執行,但JMM向程序員保證a操作將對b操作可見)。
1)如果一個操作happens-before另一個操作,那么第一個操作的執行結果將對第二個操作可見,而且第一個操作的執行順序排在第二個操作之前。
2)兩個操作之間存在happens-before關系,并不意味著Java平臺的具體實現必須要按照happens-before關系指定的順序來執行。如果重排序之后的執行結果,與按happens-before關系來執行的結果一致,那么這種重排序并不非法(JMM允許這種重排序)。
(1) 程序順序規則:線程中每個操作,happens-before該線程任意后續操作。
(2) 監視器鎖規則:對鎖的解鎖,happens-before于隨后對這個鎖的加鎖。
(3) volatile變量規則:對volatile域的寫,happens-before于任意后續對這個volatile域的讀/寫。
// 對一個volatile變量的寫操作happen-before對此變量的任意操作: volatile int a; a = 1; //1 b = a; //2 //如果線程1 執行1,“線程2”執行了2,并且“線程1”執行后,“線程2”再執行,那么符合“volatile的 //happen-before原則”所以“線程2”中的a值一定是1。
(4) 傳遞性:如果A happens-before B,且B happens-before C,那么A happens-before C。
(5) start()規則:如果線程A執行操作ThreadB.start()(啟動線程B),那么A線程的 ThreadB.start() 操作happens-before于線程B中的任意操作。
(6) Join()規則:如果線程A執行操作ThreadB.join()并成功返回,那么線程B中的任意操作happens-before于線程A從**ThreadB.join()**操作成功返回。
(7) 程序中斷規則:對線程interrupted()方法的調用先行于被中斷線程的代碼檢測到中斷時間的發生。
(8) 對象finalize規則:一個對象的初始化完成(構造函數執行結束)先行于發生它的finalize()方法的開始。
利用程序順序規則(規則1)存在三個happens-before關系:
A happens-before B
B happens-before C
A happens-before C
這里的第三個關系是利用傳遞性進行推論的。這里的第三個關系是利用傳遞性進行推論的。
volatile int var; int b; int c; b = 4; //1 var = 3; //2 c = var; //3 c = b; //4
假設“線程1”執行//1 //2這段代碼,“線程2”執行//3 //4這段代碼。如果某次的執行順序如下:
//1 //2 //3 //4。那么有如下推導( hd(a,b)表示a happen-before b):
因為有hd(//1,//2) 、hd(//3,//4) (單線程的happen-before原則)
且hd(//2,//3) (volatile的happen-before原則)
所以有 hd(//1,//3),可導出hd(//1,//4) (happen-before原則的傳遞性)
所以變量c的值最后為4 如果某次的執行順序如下:
//1 //3 //2// //4 那么最后4的結果就不能確定嘍。其原因是 //3 //2 直接符合上述八大原則中的任何一個,不能通過傳遞性推測出來什么。
A happens-before B,定義1要求A執行結果對B可見,并且A操作的執行順序在B操作之前,但與此同時利用定義中的第二條,A,B操作彼此不存在數據依賴性,兩個操作的執行順序對最終結果都不會產生影響,在不改變最終結果的前提下,允許A,B兩個操作重排序,即happens-before關系并不代表了最終的執行順序。
不管怎么重排序(編譯器和處理器為了提高并行度),單線程程序的執行結果不能被改變。編譯器,runtime 和處理器都必須遵守as-if-serial語義。
為了遵守as-if-serial語義,編譯器和處理器不會對存在數據依賴關系的操作做重排序,因為這種重排序會改變執行結果。但是,如果操作之間不存在數據依賴關系,這些操作可能被編譯器和處理器重排序。
到此,關于“Happens-Before原則和As-If-Serial語義是什么”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。