在 可見性有序性,Happens-before來搞定 文章中,happens-before 的原則之一: volatile變量規則java
對一個 volatile 域的寫, happens-before 於任意後續對這個 volatile 域的讀
按理說了解了這個規則,對 volatile 的使用就已經足夠了,可是面試官但是喜歡刨根問到底的,爲了更透徹的瞭解 volatile 的內存語義與讀寫語義,爲了面試多一些談資進而得到一些加分項,同時儘早填補前序文章留下的坑,因而乎這篇文章就這樣尷尬的誕生了面試
下面的表格你還記得嗎?(是的,你記得😂)編程
可否重排序 | 第二個操做 | 第二個操做 | 第二個操做 |
---|---|---|---|
第一個操做 | 普通讀/寫 | volatile 讀 | volatile 寫 |
普通讀/寫 | - | - | NO |
volatile 讀 | NO | NO | NO |
volatile 寫 | - | NO | NO |
上面的表格是 JMM 針對編譯器定製的 volatile 重排序的規則,那 JMM 是怎樣禁止重排序的呢?答案是內存屏障併發
不管你聽過這個名詞與否都不要緊,很簡單,且看app
爲了實現 volatile 的內存語義,編譯器在生成字節碼時,會在指令序列中插入內存屏障來禁止特定類型的處理器重排序
這句話有點抽象,試着想象內存屏障是一面高牆,若是兩個變量之間有這個屏障,那麼他們就不能互換位置(重排序)了,變量有讀(Load)有寫(Store),操做有前有後,JMM 就將內存屏障插入策略分爲 4 種:工具
1 和 2 用圖形描述以及對應表格規則就是下面這個樣子了:學習
3 和 4 用圖形描述以及對應表格規則就是下面這個樣子了:優化
其實圖形也是表格內容的體現,只不過告訴你們內存屏障是如何禁止指令重排序的,因此你們只要牢記表格內容便可
一段程序的讀寫一般不會像上面兩種狀況這樣簡單,這些屏障組合起來如何使用呢?其實一點都不難,咱們只須要將這些指令帶入到文章開頭的表格中,而後再按照程序順序拼接指令就行了spa
來看一小段程序:線程
public class VolatileBarrierExample { private int a; private volatile int v1 = 1; private volatile int v2 = 2; void readAndWrite(){ int i = v1; //第一個volatile讀 int j = v2; //第二個volatile讀 a = i + j; //普通寫 v1 = i + 1; //第一個volatile寫 v2 = j * 2; //第二個volatile寫 } }
將屏障指令帶入到程序就是這個樣子:
咱們將上圖分幾個角度來看:
到這裏你應該瞭解了 volatile 是如何經過內存屏障保證程序不被"擅自"排序的,那 volatile 是如何保證可見性的呢?
回顧一下以前文章內容中的程序,假定線程 A 先執行 writer 方法,隨後線程 B 執行 reader 方法,:
public class ReorderExample { private int x = 0; private int y = 1; private volatile boolean flag = false; public void writer(){ x = 42; //1 y = 50; //2 flag = true; //3 } public void reader(){ if (flag){ //4 System.out.println("x:" + x); //5 System.out.println("y:" + y); //6 } } }
到這裏你是否還記得以前說過的 JMM,是的,你還記得😂,當線程 A 執行 writer 方法時,且看下圖:
線程 A 將本地內存更改的變量寫回到主內存中
volatile 讀的內存語義:
當讀一個 volatile 變量時, JMM 會把該線程對應的本地內存置爲無效。線程接下來將從主內存中讀取共享變量。
因此當線程 B 執行 reader 方法時,圖形結構就變成了這個樣子:
線程 B 本地內存變量無效,從主內存中讀取變量到本地內存中,也就獲得了線程 A 更改後的結果,這就是 volatile 是如何保證可見性的
若是你看過前面的文章你就不難理解上面的兩張圖了,綜合起來講:
到這裏,面試 volatile 時,你應該有一些談資了,同時也對 volatile 的語義有了更深層次的瞭解
以前的文章提到過這樣一句話:
從內存語義的角度來講, volatile 的寫-讀
與鎖的釋放-獲取
有相同的內存效果;volatile 寫和鎖的釋放有相同的內存語義; volatile 讀與鎖的獲取有相同的內存語義
記住文中最後兩張圖, 當咱們說到 synchronized 的時候,你就會猛的理解這句話的含義了, 感興趣的能夠本身先了解 synchronized 的寫-讀語義
接下來咱們就聊一聊鎖相關的內容了,敬請期待...
https://tool.lu 是一款集成了很是多功能的在線工具,基本知足平常開發所需
歡迎持續關注公衆號:「日拱一兵」
- 前沿 Java 技術乾貨分享
- 高效工具彙總 | 回覆「工具」
- 面試問題分析與解答
- 技術資料領取 | 回覆「資料」
以讀偵探小說思惟輕鬆趣味學習 Java 技術棧相關知識,本着將複雜問題簡單化,抽象問題具體化和圖形化原則逐步分解技術問題,技術持續更新,請持續關注......