Java -- volatile關鍵字(筆記)

volatile關鍵字簡述:

Java 語言中的 volatile 變量可以被看作是一種 “程度較輕的 synchronized”;與 synchronized 塊相比市咽,volatile 變量所需的編碼較少痊银,并且運行時開銷也較少,但是它所能實現(xiàn)的功能也僅是synchronized 的一部分施绎。

volatile變量的讀取和寫入操作導致變量直接在主存中讀寫溯革。從主存中讀取和寫入到主存中比在 cpu 緩存中代價更高 贞绳。訪問volatile變量也阻止了常規(guī)的性能優(yōu)化技術對指令的重排序。所以致稀,你應該只在確實需要加強變量的可見性的時候使用volatile冈闭。

1,java-內存模型

Java內存模型規(guī)定:所有的變量都存儲在主內存中抖单。每條線程中還有自己的工作內存萎攒,線程的工作內存中保存了被該線程所使用到的變量(這些變量是從主內存中拷貝而來)。線程對變量的所有操作(讀取矛绘,賦值)都必須在工作內存中進行耍休。不同線程之間也無法直接訪問對方工作內存中的變量,線程間變量值的傳遞均需要通過主內存來完成货矮。


2羊精,java-原子性

原子性:即一個操作或者多個操作 要么全部執(zhí)行并且執(zhí)行的過程不會被任何因素打斷,要么就都不執(zhí)行囚玫。

例:銀行轉賬的過程喧锦, A 給 B 轉賬1000元 的情況。 ?

試想一下抓督,如果這2個操作不具備原子性燃少,會造成什么樣的后果。假如從賬戶A減去1000元之后本昏,操作突然中止供汛。這樣就會導致賬戶A雖然減去了1000元,但是賬戶B沒有收到這個轉過來的1000元涌穆。

在Java中,對基本數(shù)據(jù)類型的變量的讀取和賦值操作是原子性操作雀久,即這些操作是不可被中斷的宿稀,要么執(zhí)行,要么不執(zhí)行赖捌。

例:x =10 ? //語句1

? ? ? ?y = x ? ?//語句2

? ? ? ?x++ ? ? //語句3

上面只有語句1,符合原子性祝沸,直接將數(shù)值10賦值給x,也就是說線程執(zhí)行這個語句的會直接將數(shù)值10寫入到工作內存中越庇。

語句2實際上包含2個操作罩锐,它先要去讀取x的值,再將x的值寫入工作內存卤唉,雖然讀取x的值以及 將x的值寫入工作內存 這2個操作都是原子性操作涩惑,但是合起來就不是原子性操作了。

同樣的桑驱,x++ 包括3個操作:讀取x的值竭恬,進行加1操作跛蛋,寫入新的值。

也就是說痊硕,只有簡單的讀取赊级、賦值(而且必須是將數(shù)字賦值給某個變量,變量之間的相互賦值不是原子操作)才是原子操作岔绸。

理解了什么是原子操作理逊,就可以看出,什么時候可以使用``volatile``關鍵字:

- 堆變量的寫入操作盒揉,不依賴變量的當前值或者你能確保只有單個線程更新變量的值挡鞍。

- 對該變量的操作沒有包含在具有其他變量的不變式中(也就是原子操作)。

從上面可以看出预烙,Java內存模型只保證了基本讀取和賦值是原子性操作墨微,如果要實現(xiàn)更大范圍操作的原子性,可以通過synchronized和Lock來實現(xiàn)扁掸。由于synchronizedLock能夠保證任一時刻只有一個線程執(zhí)行該代碼塊翘县,那么自然就不存在原子性問題了,從而保證了原子性谴分。

3锈麸,java - 可見性

可見性是指當多個線程訪問同一個變量時,一個線程修改了這個變量的值牺蹄,其他線程能夠立即看得到修改的值忘伞。

舉個簡單的例子,看下面這段

6//線程1執(zhí)行的代碼

int i =0;

i =10;

//線程2執(zhí)行的代碼

j = i;

由上面的分析可知沙兰,當線程1執(zhí)行 i =10這句時氓奈,會先把i的初始值加載到工作內存中,然后賦值為10鼎天,那么在線程1的工作內存當中i的值變?yōu)?0了舀奶,卻沒有立即寫入到主存當中。

此時線程2執(zhí)行 j = i斋射,它會先去主存讀取i的值并加載到線程2的工作內存當中育勺,注意此時內存當中i的值還是0,那么就會使得j的值為0罗岖,而不是10.

這就是可見性問題涧至,線程1對變量i修改了之后,線程2 沒有立即看到線程1 修改的值桑包。

Java中的可見性

對于可見性南蓬,Java提供了volatile關鍵字來保證可見性。

當一個共享變量被volatile修飾時,它會保證修改的值會立即被更新到主存蓖康,當有其他線程需要讀取時铐炫,它會去內存中讀取新值。

而普通的共享變量不能保證可見性蒜焊,因為普通共享變量被修改之后倒信,什么時候被寫入主存是不確定的,當其他線程去讀取時泳梆,此時內存中可能還是原來的舊值鳖悠,因此無法保證可見性。

另外优妙,通過synchronized和Lock也能夠保證可見性乘综,synchronized和Lock能保證同一時刻只有一個線程獲取鎖然后執(zhí)行同步代碼,并且在釋放鎖之前會將對變量的修改刷新到主存當中套硼。因此可以保證可見性卡辰。

4,java - 有序性

有序性:即程序執(zhí)行的順序按照代碼的先后順序執(zhí)行邪意。

例:int ?a ?= 1;

? ? ? ?boolean b=false;

? ? ? ?a=2; ? ?/ /語句1

? ? ? ?b=true; / /語句2

上面代碼定義了一個int型變量九妈,定義了一個boolean類型變量,然后分別對兩個變量進行賦值操作雾鬼。從代碼順序上看萌朱,語句1是在語句2前面的,那么JVM在真正執(zhí)行這段代碼的時候會保證語句1一定會在語句2前面執(zhí)行嗎策菜?不一定晶疼,為什么呢?這里可能會發(fā)生指令重排序(Instruction Reorder)又憨。

指令重排序

一般來說翠霍,處理器為了提高程序運行效率可能會對輸入代碼進行優(yōu)化竟块,它不保證程序中各個語句的執(zhí)行先后順序同代碼中的順序一致壶运,但是它會保證程序最終執(zhí)行結果和代碼順序執(zhí)行結果是一致的。

比如上面的代碼中浪秘,語句1和語句2誰先執(zhí)行對最終的程序結果并沒有影響,那么就有可能在執(zhí)行過程中埠况,語句2先執(zhí)行而語句1后執(zhí)行耸携。

但是要注意,雖然處理器會對指令進行重排序辕翰,但是它會保證程序最終結果會和代碼順序執(zhí)行結果相同夺衍,那么它靠什么保證的呢?再看下面一個例子:

int ? a =10;//語句1

int ? r =2;//語句2

a = a +3;//語句3

r = a*a;//語句4

這段代碼有4個語句喜命,那么可能的一個執(zhí)行順序是: 2-->1--->3--->4

那么可不可能是這個執(zhí)行順序呢?: 語句2 語句1 語句4 語句3

不可能沟沙,因為處理器在進行重排序時是會考慮指令之間的數(shù)據(jù)依賴性河劝,如果一個指令Instruction 2必須用到Instruction 1的結果,那么處理器會保證Instruction 1會在Instruction 2之前執(zhí)行矛紫。

雖然重排序不會影響單個線程內程序執(zhí)行的結果赎瞎,但是多線程呢?下面看一個例子:

//線程1:

context = loadContext();? //語句1

inited = true;? ? ? ? ? ? //語句2

//線程2:

while(!inited ){

? ? ?sleep()

}

doSomethingwithconfig(context);

上面代碼中颊咬,由于語句1和語句2沒有數(shù)據(jù)依賴性务甥,因此可能會被重排序。假如發(fā)生了重排序喳篇,在線程1執(zhí)行過程中先執(zhí)行語句2敞临,而此是線程2會以為初始化工作已經(jīng)完成,那么就會跳出while循環(huán)麸澜,去執(zhí)行doSomethingwithconfig(context)方法挺尿,而此時context并沒有被初始化,就會導致程序出錯炊邦。

從上面可以看出编矾,指令重排序不會影響單個線程的執(zhí)行,但是會影響到線程并發(fā)執(zhí)行的正確性铣耘。

也就是說洽沟,要想并發(fā)程序正確地執(zhí)行,必須要保證原子性蜗细、可見性以及有序性裆操。只要有一個沒有被保證,就有可能會導致程序運行不正確炉媒。

在Java里面踪区,可以通過volatile關鍵字來保證一定的“有序性”。另外可以通過synchronized和Lock來保證有序性吊骤,很顯然缎岗,synchronized和Lock保證每個時刻是有一個線程執(zhí)行同步代碼,相當于是讓線程順序執(zhí)行同步代碼白粉,自然就保證了有序性传泊。

volatile變量規(guī)則:對一個變量的寫操作先行發(fā)生于后面對這個變量的讀操作。

5鸭巴,Synchronized關鍵字

Synchroized實現(xiàn)可見性

線程執(zhí)行互斥代碼的過程:

1眷细,獲得互斥鎖

2,清空工作內存

3鹃祖,從主內存拷貝變量的最新副本到工作內存

4溪椎,執(zhí)行鎖中的代碼

5,將更改后的共享變量的值刷新到住內存

6,釋放互斥鎖


volatile 和 synchronized的區(qū)別

1校读,volatile的本質是告訴jvm當前的變量在工作內存(寄存器)中的值是不確定的沼侣,需要從主內存中讀取歉秫;synchronized是鎖定當前的變量蛾洛,只有當前的線程可以訪問,其他的線程被阻塞端考。

2雅潭,volatile僅可以使用在變量級別;synchronized則可以使用在變量却特、方法扶供、類級別。

3裂明,volatile僅可以保證變量的可見性椿浓,不能保證原子性;而synchronized則可以保證變量的可見性和原子性闽晦。

4扳碍,volatile不會造成線程的阻塞;synchronized可能會造成線程的阻塞仙蛉。

5笋敞,volatile可以阻止指令重排序

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市荠瘪,隨后出現(xiàn)的幾起案子夯巷,更是在濱河造成了極大的恐慌,老刑警劉巖哀墓,帶你破解...
    沈念sama閱讀 212,080評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件趁餐,死亡現(xiàn)場離奇詭異,居然都是意外死亡篮绰,警方通過查閱死者的電腦和手機后雷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,422評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來吠各,“玉大人臀突,你說我怎么就攤上這事〖致” “怎么了惧辈?”我有些...
    開封第一講書人閱讀 157,630評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長磕瓷。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么困食? 我笑而不...
    開封第一講書人閱讀 56,554評論 1 284
  • 正文 為了忘掉前任边翁,我火速辦了婚禮,結果婚禮上硕盹,老公的妹妹穿的比我還像新娘符匾。我一直安慰自己,他們只是感情好瘩例,可當我...
    茶點故事閱讀 65,662評論 6 386
  • 文/花漫 我一把揭開白布啊胶。 她就那樣靜靜地躺著,像睡著了一般垛贤。 火紅的嫁衣襯著肌膚如雪焰坪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,856評論 1 290
  • 那天聘惦,我揣著相機與錄音某饰,去河邊找鬼。 笑死善绎,一個胖子當著我的面吹牛黔漂,可吹牛的內容都是我干的。 我是一名探鬼主播禀酱,決...
    沈念sama閱讀 39,014評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼炬守,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了剂跟?” 一聲冷哼從身側響起减途,我...
    開封第一講書人閱讀 37,752評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎浩聋,沒想到半個月后观蜗,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,212評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡衣洁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,541評論 2 327
  • 正文 我和宋清朗相戀三年墓捻,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片坊夫。...
    茶點故事閱讀 38,687評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡砖第,死狀恐怖,靈堂內的尸體忽然破棺而出环凿,到底是詐尸還是另有隱情梧兼,我是刑警寧澤,帶...
    沈念sama閱讀 34,347評論 4 331
  • 正文 年R本政府宣布智听,位于F島的核電站羽杰,受9級特大地震影響渡紫,放射性物質發(fā)生泄漏。R本人自食惡果不足惜考赛,卻給世界環(huán)境...
    茶點故事閱讀 39,973評論 3 315
  • 文/蒙蒙 一锁右、第九天 我趴在偏房一處隱蔽的房頂上張望郑临。 院中可真熱鬧恢口,春花似錦呆躲、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,777評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至鸠项,卻和暖如春干跛,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背锈锤。 一陣腳步聲響...
    開封第一講書人閱讀 32,006評論 1 266
  • 我被黑心中介騙來泰國打工驯鳖, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人久免。 一個月前我還...
    沈念sama閱讀 46,406評論 2 360
  • 正文 我出身青樓浅辙,卻偏偏與公主長得像,于是被迫代替她去往敵國和親阎姥。 傳聞我的和親對象是個殘疾皇子记舆,可洞房花燭夜當晚...
    茶點故事閱讀 43,576評論 2 349

推薦閱讀更多精彩內容