資源描述:
《java并發(fā)編程:volatile關(guān)鍵字解析-java開(kāi)發(fā)java經(jīng)驗(yàn)技巧》由會(huì)員上傳分享,免費(fèi)在線閱讀,更多相關(guān)內(nèi)容在工程資料-天天文庫(kù)。
1、Java并發(fā)編程:volatile關(guān)鍵字解析-編程開(kāi)發(fā)技術(shù)Java并發(fā)編程:volatile關(guān)鍵字解析原文出處:海了volatile這個(gè)關(guān)鍵字可能很多朋友都聽(tīng)說(shuō)過(guò),或許也都用過(guò)。在Java5之前,它是一個(gè)備受爭(zhēng)議的關(guān)鍵字,因?yàn)樵诔绦蛑惺褂盟鶗?huì)導(dǎo)致出人意料的結(jié)果。在Java5后,volatile關(guān)鍵字才得以重獲生機(jī)。volatile關(guān)鍵字雖然從字面上理解起來(lái)比較簡(jiǎn)單,但是要用好不是一件容易的事情。ft]T*volatile關(guān)鍵字是與Java的內(nèi)存模型有關(guān)的,因此在講述volatile關(guān)鍵之前,我們先來(lái)了解一下與內(nèi)存模型相關(guān)的概念和知識(shí),然后分析了vo
2、latile關(guān)鍵字的實(shí)現(xiàn)原理,最后給出了幾個(gè)使用volatile關(guān)鍵字的場(chǎng)景。以下是本文的目錄大綱:內(nèi)存模型的相關(guān)概念二.并發(fā)編程中的三個(gè)概念三.Java內(nèi)存模型四??深入剖析volatile關(guān)鍵字五?使用volatile關(guān)鍵字的場(chǎng)景若有不正之處請(qǐng)多多諒解,并歡迎批評(píng)指正。請(qǐng)尊重作者勞動(dòng)成果,轉(zhuǎn)載請(qǐng)標(biāo)明原文鏈接:http://www.cnblogs.com/dolphin0520/p/3920373.html一.內(nèi)存模型的相關(guān)概念大家都知道,計(jì)算機(jī)在執(zhí)行程序時(shí),毎條指令都是在CPU中執(zhí)行的,而執(zhí)行指令過(guò)程屮,勢(shì)必涉及到數(shù)據(jù)的讀取和寫(xiě)入。由于程序運(yùn)行過(guò)程
3、屮的臨吋數(shù)據(jù)是存放在主存(物理內(nèi)存)當(dāng)中的,這吋就存在一個(gè)問(wèn)題,出于CPU執(zhí)行速度很快,而從內(nèi)存讀取數(shù)據(jù)和向內(nèi)存寫(xiě)入數(shù)據(jù)的過(guò)程跟CPU執(zhí)行指令的速度比起來(lái)要慢的多,因此如果任何時(shí)候?qū)?shù)據(jù)的操作都耍通過(guò)和內(nèi)存的交互來(lái)進(jìn)行,會(huì)大大降低指令執(zhí)行的速度。因此在CPU里面就有了高速緩存。也就是,當(dāng)程序在運(yùn)行過(guò)程中,會(huì)將運(yùn)算需耍的數(shù)據(jù)從主存復(fù)制一份到CPU的高速緩存當(dāng)中,那么CPU進(jìn)行計(jì)算時(shí)就可以宜接從它的高速緩存讀取數(shù)據(jù)和向其中寫(xiě)入數(shù)據(jù),當(dāng)運(yùn)算結(jié)束Z后,再將高速緩存中的數(shù)據(jù)刷新到主存當(dāng)中。舉個(gè)簡(jiǎn)單的例子,比如下面的這段代碼:i二i+1;當(dāng)線程執(zhí)行這個(gè)語(yǔ)句時(shí),會(huì)先
4、從主存當(dāng)中讀取i的值,然后復(fù)制一份到高速緩存當(dāng)中,然后CPU執(zhí)行指令對(duì)i進(jìn)行加1操作,然后將數(shù)據(jù)寫(xiě)入高速緩存,最后將高速緩存屮i最新的值刷新到主存當(dāng)屮。這個(gè)代碼在單線程中運(yùn)行是沒(méi)有任何問(wèn)題的,但是在多線程中運(yùn)行就會(huì)有問(wèn)題了。在多核CPU中,每條線程可能運(yùn)行于不同的CPU屮,因此每個(gè)線程運(yùn)行吋有自己的高速緩存(對(duì)單核CPU來(lái)說(shuō),其實(shí)也會(huì)出現(xiàn)這種問(wèn)題,只不過(guò)是以線程調(diào)度的形式來(lái)分別執(zhí)行的)。本文我們以多核CPU為例。比如同時(shí)有2個(gè)線程執(zhí)行這段代碼,假如初始時(shí)i的值為0,那么我們希槊兩個(gè)線程執(zhí)行完Z后i的值變?yōu)?。但是事實(shí)會(huì)是這樣嗎?可能存在下面一種情況:初
5、始吋,兩個(gè)線程分別讀取i的值存入各自所在的CPU的高速緩存當(dāng)中,然后線程1進(jìn)行加1操作,然后把i的最新值1寫(xiě)入到內(nèi)存。此時(shí)線程2的高速緩存當(dāng)中i的值還是0,進(jìn)行加1操作Z后,i的值為1,然后線程2把i的值寫(xiě)入內(nèi)存。最終結(jié)果i的值是1,而不是2。這就是著名的緩存一致性問(wèn)題。通常稱(chēng)這種被多個(gè)線程訪問(wèn)的變量為共享變量。也就是說(shuō),如果一個(gè)變量在多個(gè)CPU中都存在緩存(一般在多線程編程時(shí)才會(huì)出現(xiàn)),那么就可能存在緩存不一致的問(wèn)題。為了解決緩存不一致性問(wèn)題,通常來(lái)說(shuō)冇以下2種解決方法:1)通過(guò)在總線加LOCK#鎖的方式2)通過(guò)緩存一致性協(xié)議這2種方式都是硬件層面上
6、提供的方式。在早期的CPU當(dāng)中,是通過(guò)在總線上加LOCK#鎖的形式來(lái)解決緩存不一致的問(wèn)題。因?yàn)镃PU和其他部件進(jìn)行通信都是通過(guò)總線來(lái)進(jìn)行的,如杲對(duì)總線加LOCK#鎖的話,也就是說(shuō)阻塞了其他CPU對(duì)其他部件訪問(wèn)(如內(nèi)存),從而使得只能冇一個(gè)CPU能使用這個(gè)變量的內(nèi)存。比如上面例子中如果一個(gè)線程在執(zhí)行i二i+1,如果在執(zhí)行這段代碼的過(guò)程屮,在總線上發(fā)出了LCOK#鎖的信號(hào),那么只有等待這段代碼完全執(zhí)行完畢之后,其他CPU才能從變量i所在的內(nèi)存讀取變量,然后進(jìn)行相應(yīng)的操作。這樣就解決了緩存不一致的問(wèn)題。但是上面的方式會(huì)有一個(gè)問(wèn)題,由于在鎖住總線期間,其他CP
7、U無(wú)法訪問(wèn)內(nèi)存,導(dǎo)致效率低下。所以就出現(xiàn)了緩存一致性I?辦議。最出名的就是Intel的MES1協(xié)議,MESI
8、辦議保證了毎個(gè)緩存中使用的共享變量的副本是一致的。它核心的思想是:當(dāng)CPU寫(xiě)數(shù)據(jù)吋,如果發(fā)現(xiàn)操作的變量是共享變量,即在其他CPU屮也存在該變量的副木,會(huì)發(fā)出信號(hào)通知其他CPU將該變量的緩存行置為無(wú)效狀態(tài),因此當(dāng)其他CPU需要讀取這個(gè)變量時(shí),發(fā)現(xiàn)白己緩存中緩存該變量的緩存行是無(wú)效的,那么它就會(huì)從內(nèi)存重新讀取。sn二.并發(fā)編程中的三個(gè)概念在并發(fā)編程中,我們通常會(huì)遇到以下三個(gè)問(wèn)題:原了性問(wèn)題,可見(jiàn)性問(wèn)題,有序性問(wèn)題。我們先看具體看一下這三個(gè)概念:1.
9、原子性原子性:即一個(gè)操作或者多個(gè)操作要么全部執(zhí)行并且執(zhí)行的過(guò)程不會(huì)被任何因索打斷,要么就都不執(zhí)