happens-before規則和as-if-serial語義

概述

本文大部分整理自《Java并發編程的藝術》,溫故而知新,加深對基礎的理解程度。

指令序列的重排序

我們在編寫代碼的時候,通常自上而下編寫,那么希望執行的順序,理論上也是逐步串行執行,但是為了提高性能,編譯器和處理器常常會對指令做重排序。

1) 編譯器優化的重排序。編譯器在不改變單線程程序語義的前提下,可以重新安排語句的執行順序。
2) 指令級并行的重排序。現代處理器采用了指令級并行技術來將多條指令重疊執行。如果不存在數據依賴性,處理器可以改變語句對應機器指令的執行順序。
3) 內存系統的重排序。由于處理器使用緩存和讀/寫緩沖區,這使得加載和存儲操作看上去可能是在亂序執行。

Java源代碼到最終實際執行的指令序列,會分別經歷下面3種重排序:

happens-before語義

JDK 5開始,Java使用新的內存模型,使用happens-before的概念來闡述操作之間的內存可見性。那到底什么是happens-before呢?

在JMM中,如果一個操作執行的結果需要對另一個操作可見,那么這兩個操作之間必須要存在happens-before關系,這里提到的兩個操作既可以是在一個線程之內,也可以是在不同線程之間。

happens-before規則如下:

程序順序規則:?對于單個線程中的每個操作,前繼操作happens-before于該線程中的任意后續操作。
監視器鎖規則:?對一個鎖的解鎖,happens-before于隨后對這個鎖的加鎖。
volatile變量規則:?對一個volatile域的寫,happens-before于任意后續對這個volatile域的讀。
傳遞性:?如果A happens-before B,且B happens-before C,那么A happens-before C

注意:

兩個操作之間具有happens-before關系,并不意味著前一個操作必須要在后一個操作之前執行,happens-before僅僅要求前一個操作(執行的結果)對后一個操作可見,且前一個操作按順序排在第二個操作之前。

happens-before與JMM的關系如圖所示:

如圖所示,一個happens-before規則對應于一個或多個編譯器和處理器重排序規則。

重排序

重排序指的是:編譯器和處理器為了優化程序性能而對指令序列進行重新排序的一種手段

如果兩個操作訪問同一個變量,且這兩個操作中有一個為寫操作,此時這兩個操作之間就存在數據依賴性。數據依賴分為下列3種類型:

上面情況,只要重排序兩個操作的執行順序,程序的執行結果就會被改變。而編譯器和處理器可能會對操作做重排序,但是編譯器和處理器在重排序時,會遵守數據依賴性,編譯器和處理器不會改變存在數據依賴關系的兩個操作的執行順序

注意:

這里所說的數據依賴性僅針對單個處理器中執行的指令序列和單個線程中執行的操作,不同處理器之間和不同線程之間的數據依賴性不被編譯器和處理器考慮

as-if-serial語義

as-if-serial語義的意思是:不管怎么重排序,單線程程序的執行結果不能被改變。編譯器、runtime和處理器都必須遵守as-if-serial語義。所以編譯器和處理器不會對存在數據依賴關系的操作做重排序,因為這種重排序會改變執行結果。但是,如果操作之間不存在數據依賴關系,這些操作就可能被編譯器和處理器重排序。

下面還是以書中的實例(計算圓的面積)進行說明:

double pi  = 3.14;           // A
double r   = 1.0;            // B
double area = pi * r * r;    // C
  • ?

上面3個操作的數據依賴關系如圖所示:

A和C之間存在數據依賴關系,同時B和C之間也存在數據依賴關系。因此在最終執行的指令序列中,C不能被重排序到A和B的前面(因為C排到A和B的前面,程序的結果將會被改變)。但A和B之間沒有數據依賴關系,編譯器和處理器可以重排序A和B之間的執行順序

該程序的兩種可能執行順序:

as-if-serial語義把單線程程序保護了起來,遵守as-if-serial語義的編譯器、runtime和處理器共同為編寫單線程程序的程序員創建了一個幻覺:單線程程序是按程序的順序來執行的

程序順序規則

根據happens-before的程序順序規則,上面計算圓的面積的示例代碼存在3個happens-before關系

1) A happens-before B。
2) B happens-before C。
3) A happens-before C。

而這里的第3個happens-before關系,是根據happens-before的傳遞性推導出來的。

注意:

這里A happens-before B,但實際執行時B卻可以排在A之前執行,JMM并不要求A一定要在B之前執行。JMM僅僅要求前一個操作(執行的結果)對后一個操作可見,且前一個操作按順序排在第二個操作之前。這里操作A的執行結果不需要對操作B可見,而且重排序操作A和操作B后的執行結果,與操作A和操作B按happens-before順序執行的結果一致。在這種情況下,JMM會認為這種重排序并不非法,JMM允許這種重排序。

重排序對多線程的影響

重排序是否會改變多線程程序的執行結果?還是借用書中的一個例子:

class ReorderExample {int a = 0;boolean flag = false;public void writer() {a = 1;                  // 1flag = true;            // 2}public void reader() {if (flag) {            // 3int i =  a * a;     // 4}}
}
  • ?

flag變量是個標記,用來標識變量a是否已被寫入。這里假設有兩個線程A和B,A首先執行writer()方法,隨后B線程接著執行reader()方法。線程B在執行操作4時,能否看到線程A在操作1對共享變量a的寫入呢?

答案是:不一定能看到

由于操作1和操作2沒有數據依賴關系,編譯器和處理器可以對這兩個操作重排序;同樣,操作3和操作4沒有數據依賴關系,編譯器和處理器也可以對這兩個操作重排序

當操作1和操作2重排序時,可能會產生什么效果?(虛箭線標識錯誤的讀操作,用實箭線標識正確的讀操作。)

如圖所示,操作1和操作2做了重排序。程序執行時,線程A首先寫標記變量flag,隨后線程B讀這個變量。由于條件判斷為真,線程B將讀取變量a。此時,變量a還沒有被線程A寫入,在這里多線程程序的語義被重排序破壞了

當操作3和操作4重排序時會產生什么效果。下面是操作3和操作4重排序后,程序執行的時序圖:

在程序中,操作3和操作4存在控制依賴關系。當代碼中存在控制依賴性時,會影響指令序列執行的并行度。為此,編譯器和處理器會采用猜測執行來克服控制相關性對并行度的影響。以處理器的猜測執行為例,執行線程B的處理器可以提前讀取并計算a*a,然后把計算結果臨時保存到一個名為重排序緩沖的硬件緩存中。當操作3的條件判斷為真時,就把該計算結果寫入變量i中。猜測執行實質上對操作3和4做了重排序,在這里重排序破壞了多線程程序的語義

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:
http://www.pswp.cn/news/386510.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/386510.shtml
英文地址,請注明出處:http://en.pswp.cn/news/386510.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

安裝nodejs

1.安裝nodejs:node-v8.12.0-x64.msi; 2.檢測是否安裝成功: 3.地址欄打開命令行:輸入 cmd回車 4.檢測node是否安裝成功:node -v 看到版本號就是安裝成功了 5.檢測npm是否成功:npm -v 是安裝成功了 6、如果npm成功了,把 package.js…

貼片晶振無源石英諧振器直插晶振

貼片晶振 貼片晶振3.579M~25MHz無源石英諧振器直插晶振 文章目錄 貼片晶振前言一、貼片晶振3.579M~25MHz無源石英諧振器直插晶振二、屬性三、技術參數總結前言 貼片晶振(Surface Mount Crystal Oscillator)是一種采用表面貼裝技術進行安裝的晶振。它的主要特點是封裝小巧、安…

這些新技術你們都知道嗎?成功收獲美團,小米安卓offer

前言 近期被兩則消息刷屏,【字節跳動持續大規模招聘,全年校招超過1萬人】【騰訊有史以來最大規模的校招啟動】當然Android崗位也包含在內,因此Android還是有很多機會的。結合往期面試的同學(主要是校招)經驗&#xff…

CompareAndSwap原子操作原理

在翻閱AQS(AbstractQueuedSynchronizer)類的過程中,發現其進行原子操作的時候采用的是CAS。涉及的代碼如下: 1: private static final Unsafe unsafe Unsafe.getUnsafe(); 2: private static final long stateOffset; 3: private static f…

STemWin移植筆記

實現將STemWin圖形庫移植到STM32F103ZET中,記錄簡單的操作步驟,以便自己后續查閱。 1/ 從官網獲取STemWin庫的壓縮包en.stemwin.zip。 2/ 解壓后,在路徑STemWin_Library_V1.2.0\Libraries\STemWinLibrary532下可以找到庫文件。 3/ 移植前的準…

這些新技術你們都知道嗎?看這一篇就夠了!

前言 現在已經進入招聘季節,本篇文章旨在分享知名互聯網企業面試官面試方法和心得,希望通過本文的閱讀能給程序員帶來不一樣的面試體驗和感受,放松面試心態,積極備戰! 面試題 PS:由于文章篇幅問題&#x…

spring boot redis 分布式鎖

yml文件 redis:host: 127.0.0.1port: 40197password: 123456timeout: 5000database: 0jedis:pool:min-idle: 0max-idle: 8max-active: 8max-wait: -1 RedisConfig.java import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.support.spring.FastJsonRedisSerializer…

mysql函數(二.數字函數)

數字函數 1.ROUND(X) 四舍五入取整數 ROUND(X,D) 四舍五入根據D幾,來保留幾位小數 (1)四舍五入取整數 select ROUND(1.567); 結果:2 (2)四舍五入根據D2,來保留2位小數 select ROUND(1.567,2)DESC; 結果:1.57 2.CEIL…

這份1307頁Android面試全套真題解析,源碼+原理+手寫框架

前言 前不久,幾個朋友聚會,談到了現在的后輩,我就說起了那個大三就已經拿到網易offer的小學弟。 這個學弟是00后,專升本進入我們學校的。進來后就非常努力,每次上課都是第一個到教室的,每次都是坐第一排&…

CAS的ABA問題描述 AtomicStampReference

CAS的ABA問題描述 在CAS操作的時候,其他線程將當前變量的值從A改成B,又改回A;CAS線程用期望值A與當前變量比較的時候,發現當前變量沒有變,于是CAS就將當前變量進行了交換操作,但其實當前變量改變過&#x…

[轉]OpenContrail 體系架構文檔

OpenContrail 體系架構文檔英文原文:http://opencontrail.org/opencontrail-architecture-documentation/ 翻譯者:KkBLuE知行合一 其微信號:kkbluepublic, SDNAP.com翻譯整理 OpenContrail 體系架構文檔 1 概述 1.1 使用案例 1…

這份354頁筆記的Android進階知識+大廠高頻面試題,絕對干貨

程序員與別的專業有所不同,其他專業都是越老越香,而程序員卻是一個例外,因為計算機技術更新太快,而且工作強度很大,因此大部分程序員只會寫 3 年代碼。3 年后要不晉升做項目經理,要么轉行,個別研…

原子性 atomic 類用法

當程序更新一個變量時,如果多線程同時更新這個變量,可能得到期望之外的值,比如變量i1,A線程更新i1,B線程也更新i1,經過兩個線程操作之后可能i不等于3,而是等于2。因為A和B線程在更新變量i的時候…

這是一份用心整理的Android面試總結,聰明人已經收藏了!

前言 本文想分享的是如何準備阿里面試的以及面試過程的所想所得,希望能幫到你。 首先,可能要讓你們失望的是,這篇文章不會有大篇幅的面試題答案。如果想要看這方面的內容,可以看我之前的文章。感謝關注 很多人準備面試的時候&a…

git 技能圖

---- 轉載于:https://www.cnblogs.com/WHWWHW/p/11136606.html

AtomicStampedReference源碼分析

之前的文章已經介紹過CAS的操作原理,它雖然能夠保證數據的原子性,但還是會有一個ABA的問題。 那么什么是ABA的問題呢?假設有一個共享變量“num”,有個線程A在第一次進行修改的時候把num的值修改成了33。修改成功之后,緊接著又立刻…

django:bootstrap table加載django返回的數據

bootstrap table加載表格數據有兩類方式: 一種通過data屬性的方式配置,一種是javascipt方式配置 這里看js配置方式: 1、當數據源為.json文件時 url參數寫上json文件的地址就行,但是json文件格式必須為json格式(2種): a:一種為json…

這是一份面向Android開發者的復習指南,成功入職字節跳動

前言 19年6月份從網易云音樂離開,放棄了留學機會,開始了人生的第一次創業,前后嘗試了兩個項目,因為個人能力與時機因素都失敗了,雖然沒能享受到創業所能夠帶來高杠桿物質上的回報,但是對個人軟技能和自我邊…

JVM啟動參數

不管是YGC還是Full GC,GC過程中都會對導致程序運行中中斷,正確的選擇不同的GC策略,調整JVM、GC的參數,可以極大的減少由于GC工作,而導致的程序運行中斷方面的問題,進而適當的提高Java程序的工作效率。但是調整GC是以個極為復雜的過程&#xf…

【UOJ 92】有向圖的強連通分量

【題目描述】: 有向圖強連通分量:在有向圖G中,如果兩個頂點vi,vj間(vi>vj)有一條從vi到vj的有向路徑,同時還有一條從vj到vi的有向路徑,則稱兩個頂點強連通(strongly connected)。如果有向圖G…