原子性 可見(jiàn)性 有序性 以及 Volatile 關(guān)鍵字使用

Java 并發(fā)編程問(wèn)題

在并發(fā)編程中试读,我們通常會(huì)遇到以下三個(gè)問(wèn)題:原子性問(wèn)題锨咙,可見(jiàn)性問(wèn)題嘶炭,有序性問(wèn)題。這些問(wèn)題發(fā)生的原因是 Java 的內(nèi)存模式?jīng)Q定的歹鱼。我們先看看 Java 的內(nèi)存模型泣栈,再來(lái)解釋一下上面的三個(gè)問(wèn)題。

Java 內(nèi)存模型

Java內(nèi)存模型規(guī)定所有的變量都是存在主存當(dāng)中(堆內(nèi)存)弥姻,每個(gè)線程都有自己的工作內(nèi)存(方法棧)南片。線程對(duì)變量的所有操作都必須在工作內(nèi)存中進(jìn)行,而不能直接對(duì)主存進(jìn)行操作庭敦。并且每個(gè)線程不能訪問(wèn)其他線程的工作內(nèi)存疼进。并且在工作內(nèi)存中進(jìn)行的操作并不是實(shí)時(shí)寫(xiě)入到主內(nèi)存中的。

原子性

原子性:即一個(gè)操作或者多個(gè)操作

要么全部執(zhí)行并且執(zhí)行的過(guò)程不會(huì)被任何因素打斷秧廉,要么就都不執(zhí)行伞广。經(jīng)典轉(zhuǎn)賬問(wèn)題不是原子性操作(因?yàn)椴⒉皇且徊讲僮?

x = 10; // 是原子性操作拣帽,不需要讀取 x 的值,只進(jìn)行賦值到內(nèi)存嚼锄,一步完成减拭,所以說(shuō)原子性操作

y = x; // 不是原子性操作,需要第一步讀取 x 的值区丑,第二步為 y 賦值到內(nèi)存中拧粪,所以不是原子性操作

在Java中,對(duì)基本數(shù)據(jù)類(lèi)型的變量的讀取和賦值操作是原子性操作刊苍,即這些操作是不可被中斷的既们,要么執(zhí)行濒析,要么不執(zhí)行正什。只有簡(jiǎn)單的讀取、賦值(而且必須是將數(shù)字賦值給某個(gè)變量号杏,變量之間的相互賦值不是原子操作)才是原子操作婴氮。

Java內(nèi)存模型只保證了基本讀取和賦值是原子性操作,如果要實(shí)現(xiàn)更大范圍操作的原子性盾致,可以通過(guò) synchronized 和 Lock 來(lái)實(shí)現(xiàn)主经。由于 synchronized 和 Lock 能夠保證任一時(shí)刻只有一個(gè)線程執(zhí)行該代碼塊,那么自然就不存在原子性問(wèn)題了庭惜,從而保證了原子性罩驻。

可見(jiàn)性

可見(jiàn)性是指當(dāng)多個(gè)線程訪問(wèn)同一個(gè)變量時(shí),一個(gè)線程修改了這個(gè)變量的值护赊,會(huì)馬上寫(xiě)入到主內(nèi)存惠遏,其他線程能夠立即看得到修改的值。

解釋?zhuān)嚎梢?jiàn)性是指骏啰,一個(gè)線程改變了這個(gè)變量的值节吮,這個(gè)值會(huì)馬上寫(xiě)入到主內(nèi)存,其他線程訪問(wèn)時(shí)會(huì)得到已經(jīng)改變了的這個(gè)值

對(duì)于可見(jiàn)性判耕,Java 提供了 volatile 關(guān)鍵字來(lái)保證可見(jiàn)性透绩。

當(dāng)一個(gè)共享變量被 volatile 修飾時(shí),它會(huì)保證修改的值會(huì)立即被更新到主存壁熄,當(dāng)有其他線程需要讀取時(shí)帚豪,會(huì)在內(nèi)存中讀取到最新值。

而普通的共享變量不能保證可見(jiàn)性草丧,因?yàn)槠胀ü蚕碜兞勘恍薷闹罄瓿迹裁磿r(shí)候被寫(xiě)入主存是不確定的,當(dāng)其他線程去讀取時(shí)方仿,此時(shí)內(nèi)存中可能還是原來(lái)的舊值固棚,因此無(wú)法保證可見(jiàn)性统翩。

另外,通過(guò) synchronized 和 Lock 也能夠保證可見(jiàn)性此洲,synchronized 和 Lock 能保證同一時(shí)刻只有一個(gè)線程獲取鎖然后執(zhí)行同步代碼厂汗,并且在釋放鎖之前會(huì)將對(duì)變量的修改刷新到主存當(dāng)中。因此可以保證可見(jiàn)性呜师。

當(dāng)線程 1 修改了一個(gè)變量的值娶桦,會(huì)馬上寫(xiě)入內(nèi)存,當(dāng)線程 2 讀取時(shí)會(huì)讀取到最新的

當(dāng)線程 1 讀取了一個(gè)變量的值汁汗,接著線程 1 阻塞衷畦,線程 2 接著讀取并修改該變量的值,線程 1 再操作時(shí)不會(huì)收到提示知牌,會(huì)操作舊的值 由于線程 1 中的操作可能不是原子操作祈争,所以可能出現(xiàn)上述問(wèn)題

有序性

有序性:即程序執(zhí)行的順序按照代碼的先后順序執(zhí)行。當(dāng)代碼執(zhí)行順序不同時(shí)角寸,多線程就會(huì)出現(xiàn)問(wèn)題菩混。

//線程1:
context = loadContext();   //語(yǔ)句1
inited = true;             //語(yǔ)句2
 
//線程2:
while(!inited ){
  sleep()
}
doSomethingwithconfig(context);

如果線程1修改 inited 為 true 但是沒(méi)有執(zhí)行語(yǔ)句1,線程2中使用 context 就會(huì)出問(wèn)題扁藕。

在Java內(nèi)存模型中沮峡,允許編譯器和處理器對(duì)指令進(jìn)行重排序,但是重排序過(guò)程不會(huì)影響到單線程程序的執(zhí)行亿柑,卻會(huì)影響到多線程并發(fā)執(zhí)行的正確性邢疙。

以上就是由于 Java 的內(nèi)存模型引起的并發(fā)編程時(shí)遇到的三個(gè)問(wèn)題,這三個(gè)問(wèn)題的解決 Java 也提供了量中場(chǎng)景的機(jī)制望薄,使用 volatile 關(guān)鍵字或者使用 synchronized 同步鎖

一疟游、volatile

一旦一個(gè)共享變量(類(lèi)的成員變量、類(lèi)的靜態(tài)成員變量)被 volatile 修飾之后式矫,那么就具備了兩層語(yǔ)義:

  1. 保證了不同線程對(duì)這個(gè)變量進(jìn)行操作時(shí)的可見(jiàn)性乡摹,即一個(gè)線程修改了某個(gè)變量的值,這新值對(duì)其他線程來(lái)說(shuō)是立即可見(jiàn)的采转。

  2. 禁止進(jìn)行指令重排序聪廉。

注意:volatile 不能保證原子性

volatile 可見(jiàn)性

這段代碼是很典型的一段代碼,很多人在中斷線程時(shí)可能都會(huì)采用這種標(biāo)記辦法故慈。但是事實(shí)上板熊,這段代碼會(huì)完全運(yùn)行正確么?即一定會(huì)將線程中斷么察绷?不一定干签,也許在大多數(shù)時(shí)候,這個(gè)代碼能夠把線程中斷拆撼,但是也有可能會(huì)導(dǎo)致無(wú)法中斷線程(雖然這個(gè)可能性很小容劳,但是只要一旦發(fā)生這種情況就會(huì)造成死循環(huán)了)喘沿。

//線程1
boolean stop = false;
while(!stop){
    doSomething();
}
 
//線程2
stop = true;

下面解釋一下這段代碼為何有可能導(dǎo)致無(wú)法中斷線程。在前面已經(jīng)解釋過(guò)竭贩,每個(gè)線程在運(yùn)行過(guò)程中都有自己的工作內(nèi)存蚜印,那么線程1在運(yùn)行的時(shí)候,會(huì)將stop變量的值拷貝一份放在自己的工作內(nèi)存當(dāng)中留量。

那么當(dāng)線程2更改了stop變量的值之后窄赋,但是還沒(méi)來(lái)得及寫(xiě)入主存當(dāng)中,線程2轉(zhuǎn)去做其他事情了楼熄,那么線程1由于不知道線程2對(duì)stop變量的更改忆绰,因此還會(huì)一直循環(huán)下去。

但是用volatile修飾之后就變得不一樣了:

第一:使用volatile關(guān)鍵字會(huì)強(qiáng)制將修改的值立即寫(xiě)入主存可岂;

第二:使用volatile關(guān)鍵字的話错敢,當(dāng)線程2進(jìn)行修改時(shí),會(huì)導(dǎo)致線程1的工作內(nèi)存中緩存變量stop的緩存行無(wú)效(反映到硬件層的話青柄,就是CPU的L1或者L2緩存中對(duì)應(yīng)的緩存行無(wú)效)伐债;

第三:由于線程1的工作內(nèi)存中緩存變量stop的緩存行無(wú)效预侯,所以線程1再次讀取變量stop的值時(shí)會(huì)去主存讀取致开。那么線程1讀取到的就是最新的正確的值。

自增并不是原子性操作萎馅,所以多個(gè)線程同時(shí)操作一個(gè)內(nèi)存中的一個(gè)數(shù)字自增操作双戳,使用 volatile 修飾該對(duì)象時(shí)會(huì)出現(xiàn)問(wèn)題

volatile 有序性

  1. 當(dāng)程序執(zhí)行到volatile變量的讀操作或者寫(xiě)操作時(shí),在其前面的操作的更改肯定全部已經(jīng)進(jìn)行糜芳,且結(jié)果已經(jīng)對(duì)后面的操作可見(jiàn)飒货;在其后面的操作肯定還沒(méi)有進(jìn)行;

  2. Java 在進(jìn)行指令優(yōu)化時(shí)峭竣,不能將在對(duì)volatile變量訪問(wèn)的語(yǔ)句放在其后面執(zhí)行塘辅,也不能把volatile變量后面的語(yǔ)句放到其前面執(zhí)行。

volatile 使用場(chǎng)景

  1. 狀態(tài)標(biāo)記量
  2. double check 單例模式時(shí)使用皆撩,使用 volatile 目的是為了保證可見(jiàn)性扣墩,當(dāng)一個(gè)線程中對(duì)對(duì)象有修改之后其他線程可見(jiàn)。

二扛吞、使用 synchronized 同步鎖

在需要同步的時(shí)候呻惕,第一選擇應(yīng)該是synchronized關(guān)鍵字,這是最安全的方式滥比,嘗試其他任何方式都是有風(fēng)險(xiǎn)的亚脆。使用同步鎖可以實(shí)現(xiàn)解決原子性、可見(jiàn)性盲泛、有序性等問(wèn)題

http://www.cnblogs.com/dolphin0520/p/3920373.html
http://blog.csdn.net/ns_code/article/details/17290021

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末濒持,一起剝皮案震驚了整個(gè)濱河市键耕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌柑营,老刑警劉巖郁竟,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異由境,居然都是意外死亡棚亩,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)虏杰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)讥蟆,“玉大人,你說(shuō)我怎么就攤上這事纺阔∪惩” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵笛钝,是天一觀的道長(zhǎng)质况。 經(jīng)常有香客問(wèn)我,道長(zhǎng)玻靡,這世上最難降的妖魔是什么结榄? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮囤捻,結(jié)果婚禮上臼朗,老公的妹妹穿的比我還像新娘。我一直安慰自己蝎土,他們只是感情好视哑,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著誊涯,像睡著了一般挡毅。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上暴构,一...
    開(kāi)封第一講書(shū)人閱讀 51,718評(píng)論 1 305
  • 那天跪呈,我揣著相機(jī)與錄音,去河邊找鬼丹壕。 笑死庆械,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的菌赖。 我是一名探鬼主播缭乘,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了堕绩?” 一聲冷哼從身側(cè)響起策幼,我...
    開(kāi)封第一講書(shū)人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎奴紧,沒(méi)想到半個(gè)月后特姐,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡黍氮,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年唐含,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片沫浆。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡捷枯,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出专执,到底是詐尸還是另有隱情淮捆,我是刑警寧澤,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布本股,位于F島的核電站攀痊,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏拄显。R本人自食惡果不足惜苟径,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望凿叠。 院中可真熱鬧涩笤,春花似錦、人聲如沸盒件。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)炒刁。三九已至,卻和暖如春誊稚,著一層夾襖步出監(jiān)牢的瞬間翔始,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工里伯, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留城瞎,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓疾瓮,卻偏偏與公主長(zhǎng)得像脖镀,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子狼电,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容