Java volatile如何實(shí)現(xiàn)禁止指令重排
計(jì)算機(jī)在執(zhí)行程序時(shí),為了提高性能,編譯器和處理器常常會(huì)對(duì)指令重排,一般分為以下三種:
源代碼 -> 編譯器優(yōu)化的重排 -> 指令并行的重排 -> 內(nèi)存系統(tǒng)的重排 -> 最終執(zhí)行指令
單線程環(huán)境里面確保最終執(zhí)行結(jié)果和代碼順序的結(jié)果一致
處理器在進(jìn)行重排序時(shí),必須要考慮指令之間的數(shù)據(jù)依賴性
多線程環(huán)境中線程交替執(zhí)行,由于編譯器優(yōu)化重排的存在,兩個(gè)線程中使用的變量能否保證一致性是無(wú)法確定的,結(jié)果無(wú)法預(yù)測(cè)。
指令重排 - example 1
public void mySort() { int x = 11; int y = 12; x = x + 5; y = x * x; }
按照正常單線程環(huán)境,執(zhí)行順序是 1 2 3 4
但是在多線程環(huán)境下,可能出現(xiàn)以下的順序:
2 1 3 4
1 3 2 4
上述的過(guò)程就可以當(dāng)做是指令的重排,即內(nèi)部執(zhí)行順序,和我們的代碼順序不一樣
但是指令重排也是有限制的,即不會(huì)出現(xiàn)下面的順序
4 3 2 1
因?yàn)樘幚砥髟谶M(jìn)行重排時(shí)候,必須考慮到指令之間的數(shù)據(jù)依賴性
因?yàn)椴襟E 4:需要依賴于 y的申明,以及x的申明,故因?yàn)榇嬖跀?shù)據(jù)依賴,無(wú)法首先執(zhí)行
例子
int a,b,x,y = 0
線程1 | 線程2 |
---|---|
x = a; | y = b; |
b = 1; | a = 2; |
x = 0; y = 0 |
因?yàn)樯厦娴拇a,不存在數(shù)據(jù)的依賴性,因此編譯器可能對(duì)數(shù)據(jù)進(jìn)行重排
線程1 | 線程2 |
---|---|
b = 1; | a = 2; |
x = a; | y = b; |
x = 2; y = 1 |
這樣造成的結(jié)果,和最開(kāi)始的就不一致了,這就是導(dǎo)致重排后,結(jié)果和最開(kāi)始的不一樣,因此為了防止這種結(jié)果出現(xiàn),volatile就規(guī)定禁止指令重排,為了保證數(shù)據(jù)的一致性
指令重排 - example 2
比如下面這段代碼
public class ResortSeqDemo { int a= 0; boolean flag = false; public void method01() { a = 1; flag = true; } public void method02() { if(flag) { a = a + 5; System.out.println("reValue:" + a); } } }
我們按照正常的順序,分別調(diào)用method01() 和 method02() 那么,最終輸出就是 a = 6
但是如果在多線程環(huán)境下,因?yàn)榉椒? 和 方法2,他們之間不能存在數(shù)據(jù)依賴的問(wèn)題,因此原先的順序可能是
a = 1;
flag = true;a = a + 5;
System.out.println("reValue:" + a);
但是在經(jīng)過(guò)編譯器,指令,或者內(nèi)存的重排后,可能會(huì)出現(xiàn)這樣的情況
flag = true;
a = a + 5;
System.out.println("reValue:" + a);a = 1;
也就是先執(zhí)行 flag = true后,另外一個(gè)線程馬上調(diào)用方法2,滿足 flag的判斷,最終讓a + 5,結(jié)果為5,這樣同樣出現(xiàn)了數(shù)據(jù)不一致的問(wèn)題
為什么會(huì)出現(xiàn)這個(gè)結(jié)果:多線程環(huán)境中線程交替執(zhí)行,由于編譯器優(yōu)化重排的存在,兩個(gè)線程中使用的變量能否保證一致性是無(wú)法確定的,結(jié)果無(wú)法預(yù)測(cè)。
這樣就需要通過(guò)volatile來(lái)修飾,來(lái)保證線程安全性
Volatile針對(duì)指令重排做了啥
Volatile實(shí)現(xiàn)禁止指令重排優(yōu)化,從而避免了多線程環(huán)境下程序出現(xiàn)亂序執(zhí)行的現(xiàn)象
首先了解一個(gè)概念,內(nèi)存屏障(Memory Barrier)又稱內(nèi)存柵欄,是一個(gè)CPU指令,它的作用有兩個(gè):
保證特定操作的順序保證某些變量的內(nèi)存可見(jiàn)性(利用該特性實(shí)現(xiàn)volatile的內(nèi)存可見(jiàn)性)
由于編譯器和處理器都能執(zhí)行指令重排的優(yōu)化,如果在指令鍵插入一條Memory Barrier則會(huì)告訴編譯器和CPU,不管什么指令都不能和這條Memory Barrier指令重排序,也就是說(shuō),通過(guò)插入內(nèi)存屏障前后的指令執(zhí)行重排序優(yōu)化。內(nèi)存屏障另外一個(gè)作用是刷新出各種CPU的緩存數(shù),因此任何cpu上的線程都能讀取到這些數(shù)據(jù)的最新版本
也就是在Volatile的寫(xiě)和讀的時(shí)候,加入屏障,防止出現(xiàn)指令重排線程安全得到保證
工作內(nèi)存與主內(nèi)存同步延遲現(xiàn)象導(dǎo)致的可見(jiàn)性問(wèn)題
- 可以使用synchronized或volatile關(guān)鍵字解決,它們都可以使得一個(gè)線程修改后的變量立即對(duì)其他線程可見(jiàn)。
- 對(duì)于指令重排導(dǎo)致的可見(jiàn)性問(wèn)題和有序性問(wèn)題
- 可以利用volatile關(guān)鍵字解決,因?yàn)関olatile的另一個(gè)作用就是禁止重排序優(yōu)化。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
利用Java+Selenium+OpenCV模擬實(shí)現(xiàn)網(wǎng)頁(yè)滑動(dòng)驗(yàn)證
目前很多網(wǎng)頁(yè)都有滑動(dòng)驗(yàn)證,目的就是防止不良爬蟲(chóng)扒他們網(wǎng)站的數(shù)據(jù)。本文將介紹通過(guò)Java Selenium OpenCV解決網(wǎng)頁(yè)滑塊驗(yàn)證,需要的可以參考一下2022-01-01Java內(nèi)存模型之happens-before概念詳解
happens-before原則非常重要,它是判斷數(shù)據(jù)是否存在競(jìng)爭(zhēng)、線程是否安全的主要依據(jù),依靠這個(gè)原則,我們解決在并發(fā)環(huán)境下兩操作之間是否可能存在沖突的所有問(wèn)題。下面我們就一個(gè)簡(jiǎn)單的例子稍微了解下happens-before知識(shí),感興趣的朋友一起看看吧2021-06-06詳解spring mvc4使用及json 日期轉(zhuǎn)換解決方案
本篇文章主要介紹了spring mvc4使用及json 日期轉(zhuǎn)換解決方案,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-01-01Java Swing GroupLayout分組布局的實(shí)現(xiàn)代碼
這篇文章主要介紹了Java Swing GroupLayout分組布局的實(shí)現(xiàn)代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12Java實(shí)現(xiàn)簡(jiǎn)易界面通訊錄
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)簡(jiǎn)易界面通訊錄,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04java實(shí)現(xiàn)任意矩陣Strassen算法
這篇文章主要介紹了java實(shí)現(xiàn)任意矩陣Strassen算法的相關(guān)資料,需要的朋友可以參考下2016-02-02SpringBoot中對(duì)應(yīng)2.0.x版本的Redis配置詳解
這篇文章主要為大家介紹了SpringBoot中對(duì)應(yīng)2.0.x版本的Redis配置詳解,文中的實(shí)現(xiàn)步驟講解詳細(xì),感興趣的小伙伴們可以了解一下2022-06-06mybatis 通過(guò)攔截器打印完整的sql語(yǔ)句以及執(zhí)行結(jié)果操作
這篇文章主要介紹了mybatis 通過(guò)攔截器打印完整的sql語(yǔ)句以及執(zhí)行結(jié)果操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-10-10