并發(fā)和多線程-說(shuō)說(shuō)面試骋谛兀考平時(shí)少用的volatile

說(shuō)到volatile侈玄,一些參加過(guò)面試的同學(xué)對(duì)此肯定不陌生。

它是面試官口中的惩患眨客,但是平時(shí)的編碼卻很少打照面(起碼呈昔,我是這樣的)。

最近的面試肝劲,我也經(jīng)常會(huì)問(wèn)到volatile相關(guān)的問(wèn)題辞槐,比如volatile和sychronized的區(qū)別粘室;volatile的使用場(chǎng)景volatile的實(shí)現(xiàn)原理等等鹿榜。

問(wèn)這些問(wèn)題其實(shí)主要還是考察多線程、鎖等方便的知識(shí)儲(chǔ)備奥裸。雖然volatile在我們?nèi)粘>幋a使用不多沪袭,但是他的實(shí)現(xiàn)思想以及背后牽扯的一些概念還是值得我們學(xué)習(xí)和思考的。

舉例

首先我們來(lái)看一個(gè)例子


<pre style="margin: 0.5em 0px; padding: 0.4em 0.6em; border-radius: 8px; background: rgb(255, 255, 255); color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; font-family: Menlo; font-size: 9pt;">public class VolatileExample extends Thread {

    private static boolean flag = false;

    public void run() {
        while (!flag) {
        }
    }

    public static void main(String[] args) throws Exception {
        new VolatileExample().start();
        Thread.sleep(100);
        flag = true;
    }
}</pre>

這段代碼并不長(zhǎng)侠鳄。

  • 聲明了一個(gè)布爾類型的靜態(tài)變量flag畦攘,初始值為false十电;

  • main函數(shù)中啟動(dòng)了一個(gè)線程VolatileExample鹃骂,集成自Thread類;

  • 除去VolatileExample線程静盅,當(dāng)然還有一個(gè)主線程main寝殴;

  • 主線程sleep 100毫秒蚣常,并在其后置flag為true抵蚊;

那么,你覺(jué)得運(yùn)行main會(huì)怎樣

  • 順利跑完所有代碼贞绳,結(jié)束冈闭?

  • 程序一直在等待萎攒,不會(huì)結(jié)束臭猜?

我們看下運(yùn)行的結(jié)果

image

從結(jié)果可以發(fā)現(xiàn)蔑歌,主線程運(yùn)行結(jié)束后次屠,界面中左邊的紅點(diǎn)并沒(méi)有消失劫灶,表示程序還沒(méi)有結(jié)束掖桦。

這是因?yàn)閱?dòng)的VolatileExample線程讀取到的flag一直都是false。

雖然主線程中flag已經(jīng)被賦值為true涌穆,但是VolatileExample線程卻視而不見(jiàn)宿稀,這是為什么呢赖捌,這就涉及到一個(gè)可見(jiàn)性的問(wèn)題越庇。

可見(jiàn)性

這里假設(shè)大家都是對(duì)線程以及鎖等知識(shí)有一定的了解

sychronized我們應(yīng)該都用過(guò)或者了解過(guò),這是為了在多線程環(huán)境下對(duì)共享資源添加一個(gè)標(biāo)識(shí)涩惑,用于鎖住共享資源搬味,防止多線程同時(shí)進(jìn)入對(duì)數(shù)據(jù)操作產(chǎn)生不一致的現(xiàn)象碰纬。

我們平時(shí)說(shuō)的加鎖其實(shí)表面上是為了達(dá)到一種互斥的效果,也就是對(duì)于同時(shí)存在的線程A和線程B悦析,如果這時(shí)候線程A或者鎖强戴,則線程B只能在旁邊乖乖的等骑歹,這就是一種互斥,有你沒(méi)我扁掸,有我沒(méi)你谴分!

但是加鎖的本質(zhì)是解決了可見(jiàn)性的問(wèn)題镀脂。具體先不說(shuō)這個(gè)問(wèn)題,我們先結(jié)合上面的例子來(lái)說(shuō)說(shuō)可見(jiàn)性的問(wèn)題沙兰。后面在結(jié)合可見(jiàn)性可能更好理解加鎖的本質(zhì)僧凰。

在java內(nèi)存模型中训措,是分為主內(nèi)存工作內(nèi)存兩塊的绩鸣。

主內(nèi)存呀闻,主要是存儲(chǔ)各個(gè)線程都會(huì)用到共享變量等捡多。

對(duì)于每個(gè)線程都有自己的一個(gè)存儲(chǔ)變量的地方,就是工作內(nèi)存垒手。各個(gè)線程之間的工作內(nèi)存是相互獨(dú)立的科贬,不可見(jiàn)的榜掌。

到這里,你可能已經(jīng)知道上面例子的程序?yàn)槭裁匆恢背鲇谶\(yùn)行的狀態(tài)沒(méi)有終止了套硼。沒(méi)錯(cuò),VolatileExample線程讀取的flag值是自己線程中存儲(chǔ)在工作內(nèi)存中的值熟菲,主線程中的flag值雖然更新了抄罕,但是對(duì)于VolatileExample是不可見(jiàn)呆贿、無(wú)感知的做入。

所以竟块,這時(shí)候volatile關(guān)鍵字就排上用場(chǎng)了浪秘。我們?cè)趂lag變量錢添加volatile關(guān)鍵字修飾埠况。再次運(yùn)行程序

image

效果顯而易見(jiàn)夺衍,主線程和VolatileExample線程一同結(jié)束喜命。

這是因?yàn)槭褂胿olatile關(guān)鍵字修飾矛紫,該變量是強(qiáng)制要求從主內(nèi)存讀以及往主內(nèi)存寫含衔,這樣保證各個(gè)線程在操作這個(gè)變量的值時(shí)二庵,都是最新鮮的數(shù)據(jù)催享。

這時(shí)候杭隙,我們?cè)賮?lái)回顧剛剛說(shuō)到加鎖的本質(zhì)是解決可見(jiàn)性問(wèn)題。

volatile是強(qiáng)制讓所有線程都從一個(gè)地方操作共享資源因妙,使得原來(lái)是從每個(gè)線程的副本中讀取變量變?yōu)閺闹鲀?nèi)存讀取變量痰憎,從而解決了可見(jiàn)性問(wèn)題。

而sychronized也是解決了可見(jiàn)性問(wèn)題攀涵,它不允許同一時(shí)間有兩個(gè)線程操作同一共享資源铣耘,因?yàn)槠錈o(wú)法保證可見(jiàn)性。所以其通過(guò)獨(dú)占互斥的方式以故,保證自己執(zhí)行完之后才會(huì)有下一個(gè)執(zhí)行蜗细,這樣每次只有一個(gè)線程占有資源,也是間接的解決了可見(jiàn)性的問(wèn)題怒详。

說(shuō)到這炉媒,就不得不提另外一個(gè)問(wèn)題——原子性

原子性


private int num = 0;

num++;

上面的代碼符合原子性么?

顯然不符合昆烁,假如現(xiàn)在有線程A和線程B兩個(gè)線程,

線程A讀取num=0蜗元,準(zhǔn)備執(zhí)行num++的操作掌敬,

但是還沒(méi)有執(zhí)行“++”操作之前楷兽,線程B讀取num的值,此時(shí)值為0,之后也執(zhí)行num++的操作,

最后A和B兩個(gè)線程最終得到的值都是1闽晦,

這是不符合原子性的碱蒙。

通過(guò)如下的sychronized的修飾就符合原子性了


sychronized(this) {

    num++;

}

其道理還是因?yàn)閟ychronized的獨(dú)占性。

那么volatile是否可以保證原子性呢

答案是否定的。道理和上面沒(méi)有加sychronized的描述是一樣的阶牍。

線程A還有執(zhí)行完num++后磕瓷,線程B也來(lái)訪問(wèn)num值,得到的是0,然后執(zhí)行num++瘩例,最終還是兩個(gè)線程得到的值都是1。

那么volatile有哪些使用場(chǎng)景呢。

volatile的適用場(chǎng)景

  • volatile是在synchronized性能低下的時(shí)候提出的。如今synchronized的效率已經(jīng)大幅提升,所以volatile存在的意義不大。

  • 如今非volatile的共享變量浩聋,在訪問(wèn)不是超級(jí)頻繁的情況下坊夫,已經(jīng)和volatile修飾的變量有同樣的效果了智听。

  • volatile不能保證原子性腻惠,這點(diǎn)是大家沒(méi)太搞清楚的复哆,所以很容易出錯(cuò)锈锤。

  • volatile可以禁止重排序(sychronized也是可以的)。

其實(shí)與之相關(guān)概念還有重排序、happens-before,as-if-serial等等,限于篇幅痰滋,不再詳述。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末复隆,一起剝皮案震驚了整個(gè)濱河市挽拂,隨后出現(xiàn)的幾起案子黎侈,更是在濱河造成了極大的恐慌蛛碌,老刑警劉巖誊辉,帶你破解...
    沈念sama閱讀 222,681評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件僵驰,死亡現(xiàn)場(chǎng)離奇詭異毡鉴,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)鞍泉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)忘巧,“玉大人砚嘴,你說(shuō)我怎么就攤上這事工育〗吣” “怎么了?”我有些...
    開封第一講書人閱讀 169,421評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵是己,是天一觀的道長(zhǎng)卒废。 經(jīng)常有香客問(wèn)我沛厨,道長(zhǎng),這世上最難降的妖魔是什么升熊? 我笑而不...
    開封第一講書人閱讀 60,114評(píng)論 1 300
  • 正文 為了忘掉前任俄烁,我火速辦了婚禮绸栅,結(jié)果婚禮上级野,老公的妹妹穿的比我還像新娘。我一直安慰自己粹胯,他們只是感情好蓖柔,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,116評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著风纠,像睡著了一般况鸣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上竹观,一...
    開封第一講書人閱讀 52,713評(píng)論 1 312
  • 那天镐捧,我揣著相機(jī)與錄音,去河邊找鬼臭增。 笑死懂酱,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的誊抛。 我是一名探鬼主播列牺,決...
    沈念sama閱讀 41,170評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼拗窃!你這毒婦竟也來(lái)了瞎领?” 一聲冷哼從身側(cè)響起泌辫,我...
    開封第一講書人閱讀 40,116評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎九默,沒(méi)想到半個(gè)月后震放,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,651評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡驼修,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,714評(píng)論 3 342
  • 正文 我和宋清朗相戀三年澜搅,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片邪锌。...
    茶點(diǎn)故事閱讀 40,865評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡勉躺,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出觅丰,到底是詐尸還是另有隱情饵溅,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布妇萄,位于F島的核電站蜕企,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏冠句。R本人自食惡果不足惜轻掩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,211評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望懦底。 院中可真熱鬧唇牧,春花似錦、人聲如沸聚唐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)杆查。三九已至扮惦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間亲桦,已是汗流浹背崖蜜。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留客峭,地道東北人豫领。 一個(gè)月前我還...
    沈念sama閱讀 49,299評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像桃笙,于是被迫代替她去往敵國(guó)和親氏堤。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,870評(píng)論 2 361

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