中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

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

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java中volatile的作用是什么

發布時間:2021-06-16 16:55:34 來源:億速云 閱讀:423 作者:chen 欄目:開發技術

本篇內容介紹了“Java中volatile的作用是什么”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

volatile可見性實驗

舉個栗子

Java中volatile的作用是什么

我這里開了兩個線程,后面的線程去修改volatile變量,前面的線程不斷獲取volatile變量,

結果是會一致卡在死循環,控制臺沒有任何輸出

假如將flag讓volatile來進行修飾

Java中volatile的作用是什么

結果是:三秒后,就不會不斷打印出信息出來

注意,Thread.sleep是會刷新線程內存的,所以不要使用Thread.sleep來分別讓一個線程獲取兩次volatile變量

volatile的特性

volatile其實相當于對變量的單詞讀或寫操作加了鎖、做了同步

由于是加了鎖,所以就有前面提到的鎖的語義,即鎖的happens-before,鎖的happens-before規定了釋放鎖的操作對于后續獲得鎖操作是可見的,所以釋放鎖的線程對于后續獲得鎖的線程是可見的,意味著volatile修飾的變量的最后寫入是可以被后面獲得鎖的線程讀取的

32位的操作系統去操作64位的變量時,會分成高32位和低32位去執行,但由于鎖,會導致這個操作也是具有原子性的,因為鎖的語義決定了臨界區代碼的執行具有原子性,即必須要整個代碼塊執行完,如果沒有鎖,那么就不是原子性的,可能會被分成不連續的兩步來執行

所以,volatile變量自身是具有下面特性的

1.原子性:無論多大的變量,對其單詞讀或寫操作都是具有原子性的,但如果類似于i++這種操作就不具備原子性了,因為這本來就是兩條命令

2.可見性:操作volatile變量的線程是可以獲取前一個線程對其的修改,即當前線程總是可以看到volatile變量最后的寫入

volatile 寫與讀的內存語義

我們先來研究一下什么依賴關系需要volatile

前面提到過總共有三種依賴關系

  • 讀后寫

  • 寫后讀

  • 寫后寫

volatile是實現可見性的,所以寫后寫就不用考慮了,而且讀后寫是不需要可見性的,所以需要可見性的是寫后讀

寫語義

volatile寫的內存語義如下:

當寫一個volatile變量時,JMM會把該線程對應的本地內存中的共享變量值刷新到主內存(即不僅修改了本地內存,而且還刷新到了主內存),注意,這個刷新是按緩存行的形式(64字節)

舉個栗子

兩個線程,A線程修改flag與A,flag與A原本為默認值

Java中volatile的作用是什么

所以volatile的寫是有兩個操作的,然后這兩個操作會合成一個原子操作

讀語義

volatile的讀內存語義為:當讀一個volatile變量時,JVM會把線程對應的本地內存置為無效,接下來重新去主內存中讀取共享變量,并且更新本地內存,注意:是讀的時候會置為無效,假如不讀就不會置為無效然后重新獲取

還是上面的栗子,不過多了一個線程B,線程B一開始讀的是默認值,后來再進行了一次讀取

Java中volatile的作用是什么

總結一下讀寫語義

讀寫語義對應的其實就是volatile的變量修飾后,會進行怎樣的過程

其實volatile的讀寫語義,就是線程之間的通信,所以volatile也是實現了線程之間的通信,來提供可見性

  • 線程A去寫volatile變量,實質上是線程A對其他要操控該volatile變量的其他線程發出了消息,該消息表明了線程A已經把該變量修改了,其他線程需要重新去獲取

  • 線程B去讀volatile變量時,實質上是線程B接收到了之前某個線程發出的消息(可能沒有消息,不過也認為接收到),知道這個變量改了,需要去重新獲取

  • 所以A寫B讀,就實現了兩個線程之間的通信,雖然不太嚴謹,因為可能A不寫,B也要讀

volatile的實現

前面已經提到過volatile的實現,字節碼上加了acc_volatile修飾符,然后指令層面上是使用了內存屏障,下面就來再詳細研究

volatile的內存語義實現

volatile還有一個功能就是可以防止命令重排序,也就是volatile的內存語義

為了實現volatile內存語義,JMM會限制重排序,因為重排序會讓語義出現變化,也就是會打斷與別的線程的通信,前面提到過,重排序總共有三種,而JMM會限制編譯器重排序與處理器重排序,并不會限制內存重排序

單純看表,很難去辨別為什么,所以下面只看不發生重排序的部分

  • 當第二個操作是volatile寫時,無論第一個操作是什么,都不能發生重排序,保證了volatile寫之前的操作不會被重排序到寫后面

  • 當第一個操作是volatile讀的時候,無論第二個操作是什么,都不能發生重排序,保證了volatile讀之后的操作不會被重排序到讀之前

  • 當第一個操作為volatile寫的時候,且第二個操作是volatile讀的時候,是不可以發生重排序

第三個比較容易理解,因為volatile寫會影響后面volatile讀的嘛,先寫后讀跟線讀后寫是完全不一樣的,所以兩次操作分別為volatile讀和volatile寫或volatile寫和volatile讀都是不允許重排序的

關鍵在于前兩條怎么理解

其實都是因為volatile的讀語義,每次volatile讀都會使緩存行失效,需要去重新獲取緩存行,緩存行中不僅有volatile變量,還有其他共享變量

現在回到第二條

  • 當第一個操作為volatile讀的時候,后面也是普通讀,重排序是沒有問題,但如果后面是普通寫,普通寫后續可能是會刷新進主存中的,此時volatile讀是會出現問題的

  • 當第一個操作為volatile讀的時候,第二個操作也為volatile讀的時候,會形成兩次新的緩存行,而每次緩存行相同變量對應的值都可能不一樣,此時如果發生重排序,就會出現不一致,比如,不發生重排序時,從第一次新的緩存行里面讀A,從第二次新的緩存行里面讀B,發生了重排序后,就是從第一次新的緩存行里面讀B2,從第二次新的緩存行里面讀A2,B與B2是不一樣的,A于A2也是不一樣的,所以不可以重排序

現在回到第一條

  • 當第一個操作為volatile寫的時候,會直接修改主存,影響后面的volatile讀,所以對于第二個操作為volatile讀是不可以重排序的

  • 當第一個操作為volatile寫的時候,會直接修改主存,是會對其他線程造成影響的,同時重排序的話,會造成結果不一致,所以也不可以重排序volatile寫

  • 當第一個操作為volatile寫的時候,可以普通讀,但不可以普通寫,因為普通寫后面也會更新到主存中去,重排序也是會導致結果不一致的

接下來關于不需要重排序

  • 普通讀寫和普通讀寫之前沒有volatile要求,所以可以重排序,當然這會導致并發問題

  • 普通讀寫和volatile讀之間,只有一個volatile讀要求,這個讀要求不會被普通讀寫影響,所以也是可以重排序,不過對于普通讀寫部分會產生并發問題

為了實現內存語義,編譯器在生成字節碼時,會在指令序列中插入內存屏障來禁止特定類型的處理器重排序,也就是上面提到的限制重排序的類型,對于執行效率來說,屏障數越少越好,但讓JMM去動態發現最優的屏障布置是不可能的,所以采用了保守策略的JMM內存屏障和插入策略

1.在每一個volatile寫操作的前面插入一個StoreStore屏障,保證了在volatile寫操作之前,上面的所有寫操作已經執行完成,并且都刷新到主存中

2.在每一個volatile寫操作的后面插入一個StoreLoad屏障,保證了必須執行完volatile寫操作,下面的讀操作才可以執行

3.在每一個volatile讀操作的后面插入一個LoadLoad屏障,保證了在volatile讀之前,上面的所有讀操作都要完成

4.在每一個volatile讀操作的后面插入一個LoadStore屏障,保證了下面的寫操作,必須要等待volatile讀操作完成才可以繼續

由于第一次操作為普通讀,第二次操作為volatile讀是允許發生重排序的,所以volatile讀前面不需要加內存屏障

“Java中volatile的作用是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

会东县| 无锡市| 明光市| 崇义县| 浦县| 招远市| 兰西县| 托克逊县| 蓬溪县| 怀远县| 沙雅县| 宁明县| 玛沁县| 乌审旗| 定安县| 乳山市| 宝鸡市| 达拉特旗| 民县| 张家界市| 弥渡县| 花莲县| 金平| 朝阳区| 高平市| 天津市| 贵南县| 长子县| 无极县| 波密县| 宾阳县| 长岭县| 鄂伦春自治旗| 大渡口区| 连平县| 康平县| 伊川县| 河北区| 抚远县| 巴中市| 彰化县|