volatile關(guān)鍵字淺析

一俯画、內(nèi)存模型 </br>

??????程序運(yùn)行過程中的臨時(shí)數(shù)據(jù)存放在主內(nèi)存(物理內(nèi)存)當(dāng)中的耕魄。而從內(nèi)存中讀寫的速度跟CPU執(zhí)行指令的速度比起來要慢的多添谊,如果任何時(shí)候?qū)?shù)據(jù)的操作都要通過和內(nèi)存的交互來進(jìn)行痪伦,會(huì)大大降低指令的執(zhí)行速度。因此CPU里面存在高速緩存遭贸。 </br>
??????在程序運(yùn)行的過程中戈咳,CPU會(huì)將運(yùn)算需要的數(shù)據(jù)從主存復(fù)制一份到其高速緩存中。CPU在進(jìn)行計(jì)算時(shí)壕吹,直接從其高速緩存讀寫數(shù)據(jù)著蛙。當(dāng)運(yùn)算結(jié)束之后,再將高速緩存的數(shù)據(jù)刷新到主存當(dāng)中算利。 </br>
??????當(dāng)一個(gè)變量在多個(gè)CPU都存在緩存時(shí),就有可能存在緩存不一致的問題泳姐。解決辦法: </br>

1)效拭、通過在總線加LOCK#鎖 </br>

??????因?yàn)镃PU和其他部件進(jìn)行通訊時(shí)都是通過總線進(jìn)行的,如果對(duì)總線加LOCK#鎖,阻塞了其他CPU對(duì)其他部件的訪問(如主存)缎患。從而使只有一個(gè)CPU能使用這個(gè)變量的內(nèi)存慕的。 </br>
??????但在鎖住總線期間,其他CPU無法訪問內(nèi)存挤渔,導(dǎo)致效率低下肮街。 </br>

2)、緩存一致性協(xié)議

??????Intel的MESI協(xié)議:當(dāng)CPU寫數(shù)據(jù)時(shí)判导,如果發(fā)現(xiàn)操作的數(shù)據(jù)時(shí)共享變量嫉父,即在其他CPU中也存在該變量的副本。則會(huì)發(fā)送信號(hào)通知其他CPU將該變量的緩存置為無效狀態(tài)眼刃。因此其他CPU需要讀取該變量時(shí)绕辖,發(fā)現(xiàn)自身緩存中該變量的緩存是無效的,則會(huì)從主存中重新獲取擂红。 </br>

二仪际、并發(fā)中的三個(gè)概念 </br>

??????1、原子性:即一個(gè)或多個(gè)操作昵骤,要么全部執(zhí)行完畢树碱,要么都不執(zhí)行。 </br>
??????2变秦、可見性:當(dāng)多個(gè)線程訪問同一個(gè)變量時(shí)成榜,一個(gè)線程修改了這個(gè)變量的值,其他線程能夠立即看到修改的值伴栓。 </br>
??????3伦连、有序性:程序執(zhí)行的順序按照代碼的先后順序執(zhí)行。(處理器會(huì)考慮指令間的數(shù)據(jù)依賴關(guān)系來進(jìn)行重排序) </br>
?????? 如以下例子钳垮,代碼1和代碼2進(jìn)行重排序?qū)Y(jié)果并無影響惑淳,但代碼3必須在代碼1,2之后。即可能出現(xiàn)的排序順序是 1,2,3 或者 2,1,3 </br>

int a = 10;
int b = 17;
a = a + b;

三饺窿、Java內(nèi)存模型 </br>

??????Java虛擬機(jī)中定義一種Java內(nèi)存模型以屏蔽各個(gè)硬件平臺(tái)和操作系統(tǒng)的內(nèi)存訪問差異歧焦。</br>
??????Java內(nèi)存模型沒有限制執(zhí)行引擎使用處理器的寄存器或高速緩存來提升執(zhí)行指令,也沒有限制編譯器對(duì)指令進(jìn)行重排序肚医。即Java內(nèi)存模型也會(huì)出現(xiàn)緩存一致性問題和指令重排序的問題绢馍。</br>
??????Java內(nèi)存模型規(guī)定所有的變量都是存在主存中(類似物理內(nèi)存),每個(gè)線程都有自己的工作內(nèi)存(類似CPU的高速緩存)肠套。線程對(duì)變量的所有操作必須在工作內(nèi)存中進(jìn)行舰涌,而不能直接對(duì)主存進(jìn)行操作。并且每個(gè)線程不能訪問其他線程的工作內(nèi)存你稚。</br>

1瓷耙、原子性

x = 10 //語句1
y = x  //語句2
x++    //語句3
x = x + 1  //語句4

??????只有語句1是原子性操作朱躺,其他三個(gè)語句都不是原子性操作。</br>
??????語句1直接將10賦值給x,也就是說線程執(zhí)行這個(gè)語句時(shí)會(huì)直接將數(shù)值10寫入到工作內(nèi)存中搁痛。而其他語句實(shí)際上包含2個(gè)操作长搀,先去讀取x的值,再將x的值寫入工作內(nèi)存鸡典。</br>

??????總結(jié):Java內(nèi)存模型只保證簡單的讀取和賦值(變量之間相互賦值不是原子操作)才是原子操作源请。想要保證大范圍操作的原子性,需要通過synchronized和Lock實(shí)現(xiàn)彻况,確保任意時(shí)刻只有一個(gè)線程執(zhí)行該代碼塊谁尸。</br>

2、可見性

??????Java提供volatile關(guān)鍵字保證可見性疗垛。</br>
??????當(dāng)一個(gè)共享變量被volatile修飾時(shí)症汹,它會(huì)保證修改的值會(huì)立即被更新到主存中,當(dāng)有其他線程需要讀取時(shí)贷腕,它會(huì)去主存讀取新值背镇。</br>
??????而普通的共享變量不能保證可見性,因?yàn)槠胀ü蚕碜兞勘恍薷暮笤笊眩裁磿r(shí)候?qū)懭胫鞔媸遣淮_定的瞒斩。當(dāng)其他線程去讀取,此時(shí)內(nèi)存可能還是原來的舊值涮总。因此無法保證可見性胸囱。</br>
??????通過synchronized和Lock也能夠保證可見性,synchronized和Lock能保證同一時(shí)刻只有一個(gè)線程獲取鎖瀑梗,然后執(zhí)行同步代碼烹笔,并且釋放鎖之前會(huì)將變量的修改刷新到主存當(dāng)中,因此可以確迸桌觯可見性谤职。</br>

3、有序性

??????在Java內(nèi)存模型中亿鲜,允許編譯器對(duì)指令進(jìn)行重排序允蜈,但是重排序不會(huì)影響到單線程的執(zhí)行,卻影響到多線程并發(fā)執(zhí)行的正確性蒿柳。</br>

比如new對(duì)象時(shí)饶套,會(huì)進(jìn)行三件事件:</br>
??????(1)、給實(shí)例分配內(nèi)存垒探;</br>
??????(2)妓蛮、調(diào)用構(gòu)造方法,初始化成員變量圾叼。</br>
??????(3)蛤克、將對(duì)象指向分配的內(nèi)存空間扔仓。</br>
而在JVM中(2)和(3)的順序是無法被保證的,只能通過volalite保證其有序性咖耘。

四、深入剖析volatile關(guān)鍵字 </br>

1)保證不同線程對(duì)變量進(jìn)行操作時(shí)的可見性(即一個(gè)線程修改某個(gè)變量的值撬码,這新值對(duì)其他線程來說是立即可見的)

2)禁止進(jìn)行指令重排序儿倒。

volatile的原理和實(shí)現(xiàn)機(jī)制:</br>
??????“觀察加入volalite關(guān)鍵字和沒有加入volalite關(guān)鍵字鎖生成的匯編代碼發(fā)現(xiàn),加入volatile關(guān)鍵字時(shí)呜笑,會(huì)多出一個(gè)lock前綴指令”</br>
??????lock前準(zhǔn)指令實(shí)際上相當(dāng)于一個(gè)內(nèi)存屏障夫否,內(nèi)存屏障會(huì)提供3個(gè)功能:</br>
??????1、確保指令重排序時(shí)叫胁,不會(huì)把其后面的指令排到內(nèi)存屏障之前的位置凰慈,也不會(huì)把前面的指令排到內(nèi)存屏障的后面。</br>
??????2驼鹅、強(qiáng)制對(duì)緩存的修改操作立即寫入內(nèi)存微谓。</br>
??????3、如果是寫操作输钩,會(huì)導(dǎo)致其他CPU中對(duì)應(yīng)的緩存無效豺型。</br>

五、使用volatile關(guān)鍵字的場景 </br>

??????synchronized關(guān)鍵字是防止多個(gè)線程同時(shí)執(zhí)行一段代碼买乃,但會(huì)影響執(zhí)行效率姻氨。而volatile關(guān)鍵字在某些情況下性能優(yōu)于synchronized,但不能替代synchronized,因?yàn)関olatile不能提供原子性剪验。</br>
??????1)對(duì)變量的寫操作不依賴于當(dāng)前值</br>
??????2)該變量沒有包含在具有其他變量的不變式中</br>

個(gè)人總結(jié):

??????配合計(jì)算機(jī)的內(nèi)存模型肴焊,可以很好的理解Java的內(nèi)存模型。以及了解到功戚,可見性在高并發(fā)時(shí)的重要性娶眷。</br>
??????在確保某個(gè)變量(比如某個(gè)flag值)在單線程進(jìn)行修改操作時(shí),可以使用volatile確保該變量的可見性疫铜。相比synchronized效率有所提升茂浮。</br>
??????在單例DCL中,synchronized時(shí)確保了變量的有序性壳咕、可見性席揽。但并沒有確保有序性,這時(shí)就需要將變量修飾成volatile來確保有序性</br>

public class Singleton{
    private volatile static Singleton mInstance;
    private Singleton() {}
    
    public static Singleton getInstance(){
        if( mInstance == null){// 語句A
            synchronized(Singleton.class){ 
                if( mInstance == null){ // 語句B
                    mInstance = new Singleton();
                }
            }
        }
        return mInstance;
    }
}

因?yàn)楫?dāng)指令進(jìn)行重排序后會(huì)出現(xiàn)以下情況:</br>
(1)谓厘、給Singleton的實(shí)例分配內(nèi)存幌羞;</br>
(3)、將mInstance對(duì)象指向分配的內(nèi)存空間竟稳。</br>
(2)属桦、調(diào)用Singleton()的構(gòu)造方法熊痴,初始化成員變量。</br>
??????當(dāng)多線程并發(fā)時(shí)聂宾,線程A先運(yùn)行到語句A中果善,mInstance是null,線程A進(jìn)行單例對(duì)象的初始化。但因?yàn)橹噶钪嘏判蛳敌常霈F(xiàn)了(1)(3)(2)的情況巾陕,當(dāng)線程A還沒執(zhí)行完(2),也就是還沒初始化完單例對(duì)象時(shí)纪他。線程B運(yùn)行到語句A鄙煤。此時(shí)單例對(duì)象已不為null,自然語句A為false茶袒,線程B會(huì)返回一個(gè)還沒初始化完畢的mInstance對(duì)象梯刚。

參考文章:

1、https://www.cnblogs.com/dolphin0520/p/3920373.html </br>
2薪寓、《Android源碼設(shè)計(jì)模式》——《單例設(shè)計(jì)模式》

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末亡资,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子向叉,更是在濱河造成了極大的恐慌沟于,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,383評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件植康,死亡現(xiàn)場離奇詭異旷太,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)销睁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門供璧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人冻记,你說我怎么就攤上這事睡毒。” “怎么了冗栗?”我有些...
    開封第一講書人閱讀 157,852評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵演顾,是天一觀的道長。 經(jīng)常有香客問我隅居,道長钠至,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,621評(píng)論 1 284
  • 正文 為了忘掉前任胎源,我火速辦了婚禮棉钧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘涕蚤。我一直安慰自己宪卿,他們只是感情好的诵,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,741評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著佑钾,像睡著了一般西疤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上休溶,一...
    開封第一講書人閱讀 49,929評(píng)論 1 290
  • 那天瘪阁,我揣著相機(jī)與錄音,去河邊找鬼邮偎。 笑死,一個(gè)胖子當(dāng)著我的面吹牛义黎,可吹牛的內(nèi)容都是我干的禾进。 我是一名探鬼主播,決...
    沈念sama閱讀 39,076評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼廉涕,長吁一口氣:“原來是場噩夢啊……” “哼泻云!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起狐蜕,我...
    開封第一講書人閱讀 37,803評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤宠纯,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后层释,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體婆瓜,經(jīng)...
    沈念sama閱讀 44,265評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,582評(píng)論 2 327
  • 正文 我和宋清朗相戀三年贡羔,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了廉白。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,716評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡乖寒,死狀恐怖猴蹂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情楣嘁,我是刑警寧澤磅轻,帶...
    沈念sama閱讀 34,395評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站逐虚,受9級(jí)特大地震影響聋溜,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜叭爱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,039評(píng)論 3 316
  • 文/蒙蒙 一勤婚、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧涤伐,春花似錦馒胆、人聲如沸缨称。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽睦尽。三九已至,卻和暖如春型雳,著一層夾襖步出監(jiān)牢的瞬間当凡,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評(píng)論 1 266
  • 我被黑心中介騙來泰國打工纠俭, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留沿量,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,488評(píng)論 2 361
  • 正文 我出身青樓冤荆,卻偏偏與公主長得像朴则,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子钓简,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,612評(píng)論 2 350

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