volatile關(guān)鍵字總結(jié)

1气筋、什么是volatile

volatile是java的一個關(guān)鍵字,它提供了一種輕量級的同步機(jī)制旋圆。相比于重量級鎖synchronized宠默,volatile更為輕量,因為它不會引起線程上下文的切換和調(diào)度灵巧。

2光稼、volatile的兩個作用

可以禁止指令的重排序優(yōu)化。

提供多線程訪問共享變量的內(nèi)存可見性孩等。

3.1什么是指令重排

指令重排序是JVM為了優(yōu)化指令艾君,提高程序運(yùn)行效率,在不影響單線程程序執(zhí)行結(jié)果的前提下肄方,盡可能地提高并行度冰垄,例如將多條指令并行執(zhí)行或者是調(diào)整指令的執(zhí)行順序。但是在多線程的情況下,指令重排序可能會帶來問題虹茶,例如程序執(zhí)行的順序可能會被調(diào)整逝薪。在加上volatile關(guān)鍵字后可以有效解決這個問題。

下面我們舉個例子:

double r =2.1;//1

double pi =3.14;//2

double area=pi*r*r;//3

以上代碼語句的執(zhí)行順序為1-》2-》3蝴罪,但實際上順序無論是1-2-3還是2-1-3對結(jié)果并無影響董济,所以在編譯時和運(yùn)行時可以根據(jù)需要對1、2語句進(jìn)行重排序要门。

重排序是指編譯器和處理器為了優(yōu)化程序性能而對指令序列進(jìn)行排序的一種手段虏肾,重排序需要遵守一定規(guī)則:

1 不會對存在數(shù)據(jù)依賴關(guān)系的操作進(jìn)行重排序

2 重排序是為了優(yōu)化性能,但是不管怎么重排序欢搜,單線程下程序的執(zhí)行結(jié)果不能被改變

3.2 指令重排帶來的問題

基于雙重檢驗的單例模式:

public class Singleton3(

? ? private static Singleton3 instance = null;

? ? private Singleton3(){}

? ? public static Singleton3 getInstance(){

? ? ? ? if(instance==null){

? ? ? ? ? ? synchronized(Singleton3.class){

? ? ? ? ? ? ? ? if(instance==null)

? ? ? ? ? ? ? ? ? ? instance=new Singleton3();//非原子操作

????????????}

????????}

? ? ? ? return instance;

????}

)

事實上封豪,這個單例模式的實現(xiàn)方式是有問題的,問題在于instance=new Singleton3()炒瘟;并不是一個原子操作吹埠。

我們可以將其抽象成以下幾條指令:

memory=allocate();//1:分配對象的內(nèi)存空間

ctorInstance(memory); //2:初始化對象

instance = memory; //3:設(shè)置instance指向剛分配的內(nèi)存地址

可以看到,操作2依賴于操作1疮装,但操作3并不依賴于操作2缘琅,所以JVM是可以針對它們進(jìn)行指令的優(yōu)化重排序的,經(jīng)過重排序后如下:

memory=allocate();//1:分配對象的內(nèi)存空間

instance = memory; //3:設(shè)置instance指向剛分配的內(nèi)存地址

ctorInstance(memory); //2:初始化對象

指令重排之后廓推,instance執(zhí)行分配好的內(nèi)存放在了前面刷袍,而這段內(nèi)存的初始化被放在了后面,在線程A執(zhí)行這段賦值語句受啥,在初始化對象之前就已經(jīng)將其賦值給instance引用做个,恰好另一個線程進(jìn)入方法判斷instance引用不為null鸽心,然后就將其返回使用滚局,導(dǎo)致出錯。

3.3 禁止指令重排的原理

volatile關(guān)鍵字提供內(nèi)存屏障的方式來防止指令被重排顽频,編譯器在生成字節(jié)碼文件時藤肢,會在指令序列中插入內(nèi)存屏障來禁止特定類型的處理器重排序。

內(nèi)存屏障會確保指令重排序時不會把其后面的指令排到內(nèi)存屏障之前的位置糯景,也不會把前面的指令排到內(nèi)存屏障的后面嘁圈;即在執(zhí)行到內(nèi)存屏障這句指令時,在它前面的操作已經(jīng)全部完成蟀淮。

對于上面的基于雙重檢驗的單例模式最住,我們只需對其稍作修改即可令其正確運(yùn)行,我們已經(jīng)知道怠惶,問題來自于指令重排涨缚,那么我們禁止掉指令重排即可,用volatile關(guān)鍵字修飾instance變量策治,使得instance在讀脓魏、寫操作前后都會插入內(nèi)存屏障兰吟,避免重排序,完整代碼如下:

public class Singleton3(

? ? private static volatile Singleton3 instance = null;

? ? private Singleton3(){}

? ? public static Singleton3 getInstance(){

? ? ? ? if(instance==null){

? ? ? ? ? ? synchronized(Singleton3.class){

? ? ? ? ? ? ? ? if(instance==null)

? ? ? ? ? ? ? ? ? ? instance=new Singleton3();

????????????}

????????}

return instance;

????}

)

4 保證內(nèi)存可見性

4.1 什么是保證內(nèi)存可見性

java支持多個線程同時訪問一個對象或者是對象的成員變量茂翔,由于每個線程可以擁有這個變量的拷貝(雖然對象以及成員變量分配的內(nèi)存是在共享內(nèi)存中的混蔼,但是每個執(zhí)行的線程還是可以擁有一份拷貝,這樣做的目的是加速程序的執(zhí)行珊燎,這是現(xiàn)代多核處理器的一個顯著特性)惭嚣,所以程序在執(zhí)行過程中,一個線程看到的變量不一定是最新的俐末。volatile告知程序任何對該變量的訪問均需要從共享內(nèi)存中獲取料按,而對它的改變必須同步刷新回共享內(nèi)存,它能保證所有線程對變量訪問的可見性卓箫。

4.2 實現(xiàn)的具體細(xì)節(jié)

如果對聲明了volatile的變量進(jìn)行寫操作载矿,JVM就會向處理器發(fā)送一條Lock前綴的指令,將這個變量所在緩存行的數(shù)據(jù)寫回到系統(tǒng)內(nèi)存烹卒。

但是闷盔,就算寫回到內(nèi)存,如果其他處理器緩存的值還是舊的旅急,再執(zhí)行計算操作就會有問題逢勾,所以,在多處理器下藐吮,為了保證各個處理器的緩存是一致的溺拱,就會實現(xiàn)緩存一致性協(xié)議,每個處理器通過嗅探在總線上傳播的數(shù)據(jù)來檢查自己緩存的值是不是過期了谣辞,當(dāng)處理器發(fā)現(xiàn)自己緩存行對應(yīng)的內(nèi)存地址被修改迫摔,就會將當(dāng)前處理器的緩存行設(shè)置成無效狀態(tài),當(dāng)處理器對這個數(shù)據(jù)進(jìn)行修改操作的時候泥从,會重新從系統(tǒng)內(nèi)存中把數(shù)據(jù)讀到處理區(qū)緩存里句占。

具體地說,內(nèi)存可見性也是通過內(nèi)存屏障實現(xiàn)的躯嫉,它會執(zhí)行下面兩個操作:

強(qiáng)制將對緩存的修改操作立即寫入主存纱烘;

如果是寫操作,它會導(dǎo)致其他CPU中對應(yīng)的緩存行無效

5 總結(jié)

volatile提供了一種輕量級的同步機(jī)制祈餐,在訪問volatile變量時不會執(zhí)行加鎖操作擂啥,因此也就不會使執(zhí)行線程阻塞。

volatile只能確狈簦可見性哺壶,而加鎖機(jī)制既可以確保可見性又可以確保原子性。

volatile屏蔽掉了JVM中必要的代碼優(yōu)化变骡,所以在效率上比較低离赫。

相比于synchronized,雖然volatile更簡單并且開銷更低塌碌,但是它的同步性較差渊胸,而且其使用也更容易出錯。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末台妆,一起剝皮案震驚了整個濱河市翎猛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌接剩,老刑警劉巖切厘,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異懊缺,居然都是意外死亡疫稿,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進(jìn)店門鹃两,熙熙樓的掌柜王于貴愁眉苦臉地迎上來遗座,“玉大人,你說我怎么就攤上這事俊扳⊥窘” “怎么了?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵馋记,是天一觀的道長号坡。 經(jīng)常有香客問我,道長梯醒,這世上最難降的妖魔是什么宽堆? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮冤馏,結(jié)果婚禮上日麸,老公的妹妹穿的比我還像新娘寄啼。我一直安慰自己逮光,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布墩划。 她就那樣靜靜地躺著涕刚,像睡著了一般。 火紅的嫁衣襯著肌膚如雪乙帮。 梳的紋絲不亂的頭發(fā)上杜漠,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼驾茴。 笑死盼樟,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的锈至。 我是一名探鬼主播晨缴,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼峡捡!你這毒婦竟也來了击碗?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤们拙,失蹤者是張志新(化名)和其女友劉穎稍途,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體砚婆,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡械拍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了装盯。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片殊者。...
    茶點(diǎn)故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖验夯,靈堂內(nèi)的尸體忽然破棺而出猖吴,到底是詐尸還是另有隱情,我是刑警寧澤挥转,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布海蔽,位于F島的核電站,受9級特大地震影響绑谣,放射性物質(zhì)發(fā)生泄漏党窜。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一借宵、第九天 我趴在偏房一處隱蔽的房頂上張望幌衣。 院中可真熱鬧,春花似錦壤玫、人聲如沸豁护。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽楚里。三九已至,卻和暖如春猎贴,著一層夾襖步出監(jiān)牢的瞬間班缎,已是汗流浹背蝴光。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留达址,地道東北人蔑祟。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像沉唠,于是被迫代替她去往敵國和親做瞪。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評論 2 355