Java并發(fā)---volatile保證了多線程下的可見性

網(wǎng)絡(luò)费就、并發(fā)相關(guān)的知識(shí),相對(duì)其他一些編程知識(shí)點(diǎn)更難一些川队,主要是不好調(diào)試并且涉及內(nèi)容太多 力细!

所以今天就取一篇并發(fā)相關(guān)的內(nèi)容分享下,我相信大家認(rèn)真看完會(huì)有收獲的固额。

大家可以先看看這個(gè)問題眠蚂,看看這個(gè)是否有問題呢? 那里有問題呢斗躏?

image

如果你在這個(gè)問題上面停留超過5s的話逝慧,那么表示你對(duì)這塊某些知識(shí)還有點(diǎn)模糊,需要再鞏固下啄糙,下面我們一起來分析下笛臣!

1. 結(jié)論

多線程并發(fā)的同時(shí)進(jìn)行set、get操作隧饼,A線程調(diào)用set方法沈堡,B線程并不一定能對(duì)這個(gè)改變可見!Q嘌恪诞丽!

2. 分析

這個(gè)類非常簡單,里面有一個(gè)屬性贵白,有2個(gè)方法:get率拒、set方法崩泡,一個(gè)用來設(shè)置屬性值禁荒,一個(gè)用來獲取屬性值,在設(shè)置屬性方法上面加了synchronized角撞。

隱式信息:多線程并發(fā)的同時(shí)進(jìn)行set呛伴、get操作勃痴,A線程調(diào)用set方法,B線程可以里面感知到嗎热康?沛申??

說到這里姐军,問題就變成了synchronized在剛剛說的上下文下面能否保證可見性L摹!奕锌!

3. 關(guān)鍵詞synchronized的用法

  • 指定加鎖對(duì)象:對(duì)給定對(duì)象加鎖著觉,進(jìn)入同步代碼前需要獲得給定對(duì)象的鎖。

  • 直接作用于實(shí)例方法:相當(dāng)于對(duì)當(dāng)前實(shí)例加鎖惊暴,進(jìn)入同步代碼前要獲得當(dāng)前實(shí)例的鎖饼丘。

  • 直接作用于靜態(tài)方法:相當(dāng)于對(duì)當(dāng)前類加鎖,進(jìn)入同步代碼前要獲得當(dāng)前類的鎖辽话。

synchronized它的工作就是對(duì)需要同步的代碼加鎖肄鸽,使得每一次只有一個(gè)線程可以進(jìn)入同步塊(其實(shí)是一種悲觀策略)從而保證線程之間得安全性。

從這里我們可以知道油啤,我們需要分析的屬于第二類情況典徘,也就是說多個(gè)線程如果同時(shí)進(jìn)行set方法的時(shí)候,由于存在鎖益咬,所以會(huì)一個(gè)一個(gè)進(jìn)行set操作烂斋,并且是線程安全的,但是get方法并沒有加鎖础废,表示假如A線程在進(jìn)行set的同時(shí)B線程可以進(jìn)行g(shù)et操作汛骂。并且可以多個(gè)線程同時(shí)進(jìn)行g(shù)et操作,但是同一時(shí)間最多只能有一個(gè)set操作评腺。

4. Java 內(nèi)存模型 happens-before原則

JSR-133 內(nèi)存模型使用 happens-before 的概念來闡述操作之間的內(nèi)存可見性帘瞭。在 JMM 中,如果一個(gè)操作執(zhí)行的結(jié)果需要對(duì)另一個(gè)操作可見蒿讥,那么這兩個(gè)操作之間必須要存在 happens-before 關(guān)系蝶念。這里提到的兩個(gè)操作既可以是在一個(gè)線程之內(nèi),也可以是在不同線程之間芋绸。

與程序員密切相關(guān)的 happens-before 規(guī)則如下:

  • 程序順序規(guī)則:一個(gè)線程中的每個(gè)操作媒殉,happens-before 于該線程中的任意后續(xù)操作。

  • 監(jiān)視器鎖規(guī)則:對(duì)一個(gè)監(jiān)視器的解鎖摔敛,happens-before 于隨后對(duì)這個(gè)監(jiān)視器的加鎖廷蓉。

  • volatile 變量規(guī)則:對(duì)一個(gè) volatile 域的寫,happens-before 于任意后續(xù)對(duì)這個(gè) volatile 域的讀马昙。

  • 傳遞性:如果 A happens-before B桃犬,且 B happens-before C刹悴,那么 A happens-before C。

注意攒暇,兩個(gè)操作之間具有 happens-before 關(guān)系土匀,并不意味著前一個(gè)操作必須要在后一個(gè)操作之前執(zhí)行!happens-before 僅僅要求前一個(gè)操作(執(zhí)行的結(jié)果)對(duì)后一個(gè)操作可見形用,且前一個(gè)操作按順序排在第二個(gè)操作之前(the first is visible to and ordered before the second)就轧。

其中有監(jiān)視器鎖規(guī)則:對(duì)一個(gè)監(jiān)視器的解鎖,happens-before 于隨后對(duì)這個(gè)監(jiān)視器的加鎖田度。這一條钓丰,僅僅只是針對(duì)synchronized的set方法,而對(duì)于get并沒有這方面的說明每币。

其實(shí)在這種上下文下面一個(gè)synchronized的set方法携丁,一個(gè)普通的get方法,a線程調(diào)用set方法兰怠,b線程并不一定能對(duì)這個(gè)改變可見梦鉴!

5. volatile

volatile可見性

前面happens-before原則就提到:volatile 變量規(guī)則:對(duì)一個(gè) volatile 域的寫,happens-before 于任意后續(xù)對(duì)這個(gè) volatile 域的讀揭保。volatile從而保證了多線程下的可見性7食取!秸侣!

volatile 禁止內(nèi)存重排序

下面是 JMM 針對(duì)編譯器制定的 volatile 重排序規(guī)則表:

image

為了實(shí)現(xiàn) volatile 的內(nèi)存語義存筏,編譯器在生成字節(jié)碼時(shí),會(huì)在指令序列中插入內(nèi)存屏障來禁止特定類型的處理器重排序味榛。

下面是基于保守策略的 JMM 內(nèi)存屏障插入策略:

  • 在每個(gè) volatile 寫操作的前面插入一個(gè) StoreStore 屏障椭坚。

  • 在每個(gè) volatile 寫操作的后面插入一個(gè) StoreLoad 屏障。

  • 在每個(gè) volatile 讀操作的后面插入一個(gè) LoadLoad 屏障搏色。

  • 在每個(gè) volatile 讀操作的后面插入一個(gè) LoadStore 屏障善茎。

下面是保守策略下,volatile 寫操作 插入內(nèi)存屏障后生成的指令序列示意圖:

image

下面是在保守策略下频轿,volatile 讀操作 插入內(nèi)存屏障后生成的指令序列示意圖:

image

上述 volatile 寫操作和 volatile 讀操作的內(nèi)存屏障插入策略非常保守垂涯。在實(shí)際執(zhí)行時(shí),只要不改變 volatile 寫-讀的內(nèi)存語義航邢,編譯器可以根據(jù)具體情況省略不必要的屏障耕赘。

雙重檢查鎖實(shí)現(xiàn)單例中就需要用到這個(gè)特性!I乓蟆操骡!

6. 模擬

通過上面的分析,其實(shí)這個(gè)題目涉及到的內(nèi)容都提到了,并且進(jìn)行了解答当娱。

雖然你知道的原因吃既,但是想模擬并不是一件容易的事情考榨!跨细,下面我們來模擬看看效果:

public class ThreadSafeCache {
   int result;
   public int getResult() { 
      return result; 
   } 
   public synchronized void setResult(int result) {
      this.result = result;
   }
   public static void main(String[] args) { 
      ThreadSafeCache threadSafeCache = new ThreadSafeCache();
      for ( int i =  0 ; i <  8 ; i++) {
         new Thread (() -> { 
             int x =  0 ;
             while (threadSafeCache.getResult() <  100) {
                    x++;
             }
             System.out.println(x);
         }).start();
       }
       try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        threadSafeCache.setResult(200);
    }
}

效果:

image

程序會(huì)一直卡在這邊不動(dòng),表示set修改的200河质,get方法并不可見<讲选!掀鹅!

添加volatile 關(guān)鍵詞觀察效果

其實(shí)例子中synchronized關(guān)鍵字可以去掉散休,僅僅用volatile即可。

image

效果:

image

代碼很快正常結(jié)束了乐尊!

結(jié)論: 線程并發(fā)的同時(shí)進(jìn)行set戚丸、get操作,A線程調(diào)用set方法扔嵌,B線程并不一定能對(duì)這個(gè)改變可見O薷!痢缎!胁勺,上面的代碼中,如果對(duì)get方法也加synchronized也是可見的独旷,還是happens-before的 監(jiān)視器鎖規(guī)則:對(duì)一個(gè)監(jiān)視器的解鎖署穗,happens-before 于隨后對(duì)這個(gè)監(jiān)視器的加鎖。 只是volatile比synchronized更輕量級(jí)嵌洼,所以本例直接用volatile案疲。但是對(duì)于符合非原子操作i++這里還是不行的還是需要synchronized。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末麻养,一起剝皮案震驚了整個(gè)濱河市络拌,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌回溺,老刑警劉巖春贸,帶你破解...
    沈念sama閱讀 221,576評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異遗遵,居然都是意外死亡萍恕,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門车要,熙熙樓的掌柜王于貴愁眉苦臉地迎上來允粤,“玉大人,你說我怎么就攤上這事±嗟妫” “怎么了司光?”我有些...
    開封第一講書人閱讀 168,017評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長悉患。 經(jīng)常有香客問我残家,道長袱衷,這世上最難降的妖魔是什么卖氨? 我笑而不...
    開封第一講書人閱讀 59,626評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮道媚,結(jié)果婚禮上陪捷,老公的妹妹穿的比我還像新娘回窘。我一直安慰自己,他們只是感情好市袖,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評(píng)論 6 397
  • 文/花漫 我一把揭開白布啡直。 她就那樣靜靜地躺著,像睡著了一般苍碟。 火紅的嫁衣襯著肌膚如雪酒觅。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,255評(píng)論 1 308
  • 那天驰怎,我揣著相機(jī)與錄音阐滩,去河邊找鬼。 笑死县忌,一個(gè)胖子當(dāng)著我的面吹牛掂榔,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播症杏,決...
    沈念sama閱讀 40,825評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼装获,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了厉颤?” 一聲冷哼從身側(cè)響起穴豫,我...
    開封第一講書人閱讀 39,729評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎逼友,沒想到半個(gè)月后精肃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,271評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡帜乞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評(píng)論 3 340
  • 正文 我和宋清朗相戀三年司抱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片黎烈。...
    茶點(diǎn)故事閱讀 40,498評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡习柠,死狀恐怖匀谣,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情资溃,我是刑警寧澤武翎,帶...
    沈念sama閱讀 36,183評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站溶锭,受9級(jí)特大地震影響宝恶,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜暖途,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評(píng)論 3 333
  • 文/蒙蒙 一卑惜、第九天 我趴在偏房一處隱蔽的房頂上張望膏执。 院中可真熱鬧驻售,春花似錦、人聲如沸更米。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽征峦。三九已至迟几,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間栏笆,已是汗流浹背类腮。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蛉加,地道東北人蚜枢。 一個(gè)月前我還...
    沈念sama閱讀 48,906評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像针饥,于是被迫代替她去往敵國和親厂抽。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評(píng)論 2 359

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