Java中volatile防止指令重排
volatile可以防止指令重排,在多線程環(huán)境下有時(shí)候我們需要使用volatile來防止指令重排,來保證代碼運(yùn)行后數(shù)據(jù)的準(zhǔn)確性
什么是指令重排?
計(jì)算機(jī)在執(zhí)行程序時(shí),為了提高性能,編譯器和處理器一般會(huì)進(jìn)行指令重排,一般分為以下三種:
指令重排有以下三個(gè)特點(diǎn):
1.單線程環(huán)境下指令重排后可以保證與順序執(zhí)行指令的結(jié)果一致(就是不進(jìn)行指令重排的情況)
//原來的執(zhí)行順序 a=1; b=0; //進(jìn)行指令重排后執(zhí)行 b=0; a=1;
這兩個(gè)順序執(zhí)行的指令結(jié)果都是a=1,b=0
2.進(jìn)行指令重排的時(shí)候要考慮指令之間的數(shù)據(jù)依賴性(某個(gè)指令的數(shù)據(jù)需要根據(jù)另一個(gè)指令的數(shù)據(jù)獲得)
//原來的執(zhí)行順序 a=0; //指令1 a=10; //指令2 b=a+1; //指令3 //進(jìn)行指令重排后 a=0; b=a+1; a=10;
此時(shí)兩種順序輸出的結(jié)果就不一樣了,這是因?yàn)橹噶?的數(shù)據(jù)依賴于指令2,單線程環(huán)境下指令重排不會(huì)出現(xiàn)這種情況。
3.多線程環(huán)境下,多個(gè)線程交替執(zhí)行,由于編譯器會(huì)進(jìn)行指令重排,結(jié)果無法預(yù)測(cè)。
為什么指令重排能夠提高性能
串行的代碼確實(shí)會(huì)按代碼語意正確的執(zhí)行(就是編寫的代碼的運(yùn)行邏輯),但是編譯器對(duì)于代碼本身的優(yōu)化卻并不一定會(huì)按實(shí)際的代碼一步一步的執(zhí)行,就比如下面這段代碼
public void process() { int a = 10; #指令1 int b = 20; #指令2 }
代碼的執(zhí)行過程一定是是int a=10然后int b=20,但是代碼轉(zhuǎn)換成計(jì)算機(jī)可以識(shí)別的指令可能是指令2,指令1。
我們知道指令的執(zhí)行可以分為這幾步:
- 取址 IF
- 譯碼和取寄存器操作數(shù) ID
- 執(zhí)行或者有效地址計(jì)算 EX (ALU邏輯計(jì)算單元)
- 存儲(chǔ)器訪問 MEM
- 寫回 WB (寄存器)
一段代碼并不是由單條指令就可以執(zhí)行完畢的,而是通過流水線技術(shù)來執(zhí)行多條指令。
流水線技術(shù)是一種將指令分解為多步,并讓不同指令的各步操作重疊,從而實(shí)現(xiàn)幾條指令并行處理,這樣就提高了指令的執(zhí)行速度
簡(jiǎn)單來說就是通過指令重排,可以使用流水線技術(shù)實(shí)現(xiàn)指令的細(xì)分,然后實(shí)現(xiàn)幾條指令的并行處理,從而提高速度
volatile是怎么禁止指令重排的?
這就涉及到一個(gè)概念內(nèi)存屏障(內(nèi)存柵欄),它是一個(gè)cpu指令,有兩個(gè)作用:
- 保證某些特定操作的執(zhí)行順序
- 保證某些變量的內(nèi)存可見性(實(shí)現(xiàn)了volatile保證可見性)
編譯器和處理器都可以進(jìn)行指令重排,那么如果我們?cè)诔绦蛑胁迦胍粭lMemery Barrier(內(nèi)存屏障),那么就會(huì)告訴編譯器和cpu不能對(duì)這條指令進(jìn)行重排,也就是說通過插入內(nèi)存屏障,使屏障前后的指令不會(huì)進(jìn)行重排優(yōu)化,內(nèi)存屏障還可以強(qiáng)制刷出cpu的緩存,因此cpu上的線程都能讀到這些數(shù)據(jù)的最新版本。
簡(jiǎn)單來說就是插入內(nèi)存屏障后告訴cpu和編譯器,這個(gè)內(nèi)存屏障前后的指令你不要給我進(jìn)行重排序
內(nèi)存屏障分為四種:
StoreStore屏障、StoreLoad屏障、LoadLoad屏障、LoadStore屏障。
- Load相當(dāng)于讀屏障
- Store相當(dāng)于寫屏障
到此這篇關(guān)于Java中volatile防止指令重排 的文章就介紹到這了,更多相關(guān)Java volatile防止指令重排 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java ThreadPool線程池的使用,線程池工具類用法說明
這篇文章主要介紹了java ThreadPool線程池的使用,線程池工具類用法說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-10-10Java中CAS機(jī)制實(shí)現(xiàn)方法詳解
傳統(tǒng)的并發(fā)控制手段如synchronized和ReentrantLock雖有效防止資源競(jìng)爭(zhēng),卻可能引起性能開銷,相比之下,CAS(CompareAndSwap)提供一種輕量級(jí)的樂觀鎖策略,通過硬件級(jí)別的原子指令實(shí)現(xiàn)無鎖并發(fā),提高性能,需要的朋友可以參考下2024-09-09java實(shí)現(xiàn)動(dòng)態(tài)圖片效果
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)動(dòng)態(tài)圖片效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-03-03Java實(shí)戰(zhàn)之多線程模擬站點(diǎn)售票
今天帶大家來練習(xí)Java實(shí)戰(zhàn),文中多線程模擬站點(diǎn)售票這個(gè)問題作了詳細(xì)的介紹,對(duì)正在學(xué)習(xí)java的小伙伴們有很好地幫助,需要的朋友可以參考下2021-05-05詳解java創(chuàng)建一個(gè)女朋友類(對(duì)象啥的new一個(gè)就是)==建造者模式,一鍵重寫
這篇文章主要介紹了java建造者模式一鍵重寫,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04Java堆&優(yōu)先級(jí)隊(duì)列示例講解(下)
這篇文章主要通過示例詳細(xì)為大家介紹Java中的堆以及優(yōu)先級(jí)隊(duì)列,文中的示例代碼講解詳細(xì),對(duì)我們了解java有一定幫助,需要的可以參考一下2022-03-03連續(xù)調(diào)用多個(gè)外部系統(tǒng)寫接口保證數(shù)據(jù)一致性的思路
今天小編就為大家分享一篇關(guān)于連續(xù)調(diào)用多個(gè)外部系統(tǒng)寫接口保證數(shù)據(jù)一致性的思路,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2018-12-12