Java程序員必知的并發(fā)編程藝術(shù)——并發(fā)機(jī)制的底層原理實(shí)現(xiàn)

Java編程語(yǔ)言允許線程訪問(wèn)共享變量,為了確保共享變量能被準(zhǔn)確和一致的更新萌业,線程應(yīng)該確保通過(guò)排他鎖單獨(dú)獲得這個(gè)變量窑业。

volatile借助Java內(nèi)存模型保證所有線程能夠看到最新的值翔怎。(內(nèi)存可見性)

實(shí)現(xiàn)原理:

將帶有volatile變量操作的Java代碼轉(zhuǎn)換成匯編代碼后彬碱,可以看到多了個(gè)lock前綴指令(X86平臺(tái)CPU指令)豆胸。這個(gè)lock指令是關(guān)鍵,在多核處理器下實(shí)現(xiàn)兩個(gè)重要操作:

1.將當(dāng)前處理器緩存行的數(shù)據(jù)寫回到系統(tǒng)內(nèi)存巷疼。

2.這個(gè)寫回內(nèi)存的操作會(huì)使其他處理器里緩存該內(nèi)存地址的數(shù)據(jù)失效

如果了解計(jì)算機(jī)組成原理晚胡,可以知道CPU為了提高處理速度,不和內(nèi)存直接進(jìn)行交互嚼沿,而是使用Cache(高速緩存估盘,通過(guò)緩存數(shù)據(jù)交互速度和內(nèi)存不是一個(gè)數(shù)量級(jí),而同時(shí)Cache的存儲(chǔ)容量也很小)骡尽。

從內(nèi)存將數(shù)據(jù)讀到緩存后遣妥,CPU進(jìn)行一系列數(shù)據(jù)操作,而操作完成時(shí)間是不可知的攀细。而JVM對(duì)帶有volatile變量進(jìn)行寫操作時(shí)箫踩,會(huì)發(fā)送Lock前綴指令,將數(shù)據(jù)從緩存行寫入到內(nèi)存谭贪。寫入內(nèi)存還不夠班套,因?yàn)槠渌€程的緩存行中數(shù)據(jù)還是舊的,Lock指令可以讓其他CPU通過(guò)監(jiān)聽在總線上的數(shù)據(jù)故河,檢查自己的緩存數(shù)據(jù)是否過(guò)期吱韭,如果緩存行的地址和總線上的地址相同,則將緩存行失效鱼的,下次該線程對(duì)這個(gè)數(shù)據(jù)操作時(shí)理盆,會(huì)重新從內(nèi)存中讀取,更新到緩存行凑阶。

2.Synchronized

Synchronized也是經(jīng)常用到的猿规,它給人的印象一般是”重量級(jí)鎖”。在JDK1.6后宙橱,對(duì)Synchronized進(jìn)行了一系列優(yōu)化姨俩,引入了偏向鎖和輕量級(jí)鎖,對(duì)鎖的存儲(chǔ)結(jié)構(gòu)和升級(jí)過(guò)程师郑。有效減少獲得鎖和釋放鎖帶來(lái)的性能消耗环葵。

Synchronized同步基礎(chǔ):

1.普通同步方法,鎖是當(dāng)前實(shí)例對(duì)象宝冕。 public synchronized void test(){…}

2.靜態(tài)同步方法张遭,鎖是當(dāng)前類的Class對(duì)象。public static synchronized void test(…){}

3.對(duì)于同步方法塊地梨,鎖是Synchronized括號(hào)中里配置的對(duì)象菊卷。synchronized(instance){…}

用javap反編譯class文件缔恳,可以看到Synchronized用的是monitorenter和monitorexit實(shí)現(xiàn)加鎖。一個(gè)monitorenter必須要有monitorexit與之對(duì)應(yīng)洁闰,所以同步方法會(huì)在異常處和方法返回處加入monitorexit指令歉甚。

<pre style="box-sizing: border-box; margin: 0px 0px 24px; padding: 0px 16px; overflow-x: auto; background-color: rgb(255, 255, 255); font-family: Consolas, Menlo, Courier, monospace; font-size: 16px; line-height: 20px; color: rgb(34, 34, 34); -webkit-tap-highlight-color: transparent; white-space: normal; text-align: start;">3: monitorenter //注意此處,進(jìn)入同步方法 4: aload_0 5: dup 6: getfield #2 // Field i:I 9: iconst_110: iadd11: putfield #2 // Field i:I14: aload_115: monitorexit //注意此處扑眉,退出同步方法</pre>

3.Java對(duì)象頭

Synchronized用到的鎖存在Java對(duì)象頭里,若對(duì)象非數(shù)組類型纸泄,用32bit存儲(chǔ)(2個(gè)字寬,32虛擬機(jī)一個(gè)字寬為4字節(jié)襟雷,一個(gè)字節(jié)8bit)

MarkWord存儲(chǔ)和鎖相關(guān)的信息:

Java程序員必知的并發(fā)編程藝術(shù)——并發(fā)機(jī)制的底層原理實(shí)現(xiàn)

鎖有四個(gè)等級(jí): 無(wú)鎖->偏向鎖->輕量級(jí)鎖->重量級(jí)鎖刃滓。如果存在競(jìng)爭(zhēng)仁烹,就會(huì)不斷升級(jí)耸弄,但不會(huì)降級(jí)。

1.偏向鎖

多數(shù)情況下卓缰,鎖不會(huì)存在競(jìng)爭(zhēng)计呈,而是同一個(gè)線程多次獲得。當(dāng)某個(gè)線程訪問(wèn)同步塊代碼時(shí)征唬,會(huì)將鎖對(duì)象和棧幀中的鎖記里存儲(chǔ)鎖偏向的線程ID捌显,以后線程在進(jìn)入和退出同步塊時(shí)不需要進(jìn)行CAS操作來(lái)加鎖和解鎖,只需簡(jiǎn)單比對(duì)一下對(duì)象頭中的MarkWord里的線程ID总寒,如果一致則表示線程獲得鎖扶歪。若不一致,再繼續(xù)測(cè)試偏向鎖的標(biāo)識(shí)是否為1:如果沒(méi)有設(shè)置(無(wú)鎖狀態(tài))摄闸,用CAS(Compare and Swap)競(jìng)爭(zhēng)鎖善镰;如果設(shè)置了,嘗試使用CAS將對(duì)象頭的偏向鎖指向當(dāng)前線程年枕。

當(dāng)有另一個(gè)線程嘗試競(jìng)爭(zhēng)鎖時(shí)炫欺,持有偏向鎖的線程才會(huì)釋放鎖。需要等待全局安全點(diǎn)(在這個(gè)時(shí)間點(diǎn)上沒(méi)有字節(jié)碼正在執(zhí)行)熏兄,它會(huì)首先暫停擁有偏向鎖的線程品洛,然后檢查持有偏向鎖的線程是否活著,如果線程不處于活動(dòng)狀態(tài)摩桶,則將對(duì)象頭設(shè)置成無(wú)鎖狀態(tài)桥状,如果線程仍然活著,擁有偏向鎖的棧會(huì)被執(zhí)行硝清,遍歷偏向?qū)ο蟮逆i記錄岛宦,棧中的鎖記錄和對(duì)象頭的Mark Word,要么重新偏向于其他線程耍缴,要么恢復(fù)到無(wú)鎖或者標(biāo)記對(duì)象不適合作為偏向鎖砾肺,最后喚醒暫停的線程挽霉。

Java程序員必知的并發(fā)編程藝術(shù)——并發(fā)機(jī)制的底層原理實(shí)現(xiàn)

Java 6,7默認(rèn)開啟偏向鎖变汪,可以通過(guò)JVM的參數(shù)-XX:-UsebiasedLocking=false關(guān)閉

2.輕量級(jí)鎖

(1)加鎖

鎖記錄存儲(chǔ)在棧楨侠坎,會(huì)將對(duì)象頭的MarkWord復(fù)制到鎖記錄。線程在執(zhí)行同步塊時(shí)裙盾,會(huì)嘗試用CAS將對(duì)象頭的MarkWord替換為指向鎖記錄的指針实胸,若成功,獲得鎖番官;失敗表示其他線程競(jìng)爭(zhēng)鎖庐完,當(dāng)前線程嘗試使用自旋獲取鎖。

(2)解鎖

類似于加鎖反向操作徘熔,會(huì)將鎖記錄復(fù)制會(huì)對(duì)象頭的MarkWord门躯。若成功,表示操作過(guò)程中沒(méi)有競(jìng)爭(zhēng)發(fā)生酷师;若失敗讶凉,存在競(jìng)爭(zhēng),鎖會(huì)膨脹成重量級(jí)鎖山孔。

如下圖:

Java程序員必知的并發(fā)編程藝術(shù)——并發(fā)機(jī)制的底層原理實(shí)現(xiàn)

當(dāng)膨脹到重量級(jí)鎖時(shí)懂讯,不會(huì)再通過(guò)自選獲得鎖(自旋時(shí)線程處于活動(dòng)狀態(tài),會(huì)消耗CPU)台颠,而是將線程阻塞褐望,獲得鎖的線程執(zhí)行完后會(huì)釋放重量級(jí)鎖,此時(shí)喚醒因?yàn)殒i阻塞的線程串前,進(jìn)行新一輪的競(jìng)爭(zhēng)瘫里。

3.其他鎖概念

自旋鎖:

自旋鎖是采用讓當(dāng)前線程不停地的在循環(huán)體內(nèi)執(zhí)行實(shí)現(xiàn)的,當(dāng)循環(huán)的條件被其他線程改變時(shí) 才能進(jìn)入臨界區(qū)酪呻。

<pre style="box-sizing: border-box; margin: 0px 0px 24px; padding: 0px 16px; overflow-x: auto; background-color: rgb(255, 255, 255); font-family: Consolas, Menlo, Courier, monospace; font-size: 16px; line-height: 20px; color: rgb(34, 34, 34); -webkit-tap-highlight-color: transparent; white-space: normal; text-align: start;">public class SpinLock { private AtomicReference<Thread> sign =new AtomicReference<>(); public void lock(){Thread current = Thread.currentThread(); while(!sign .compareAndSet(null, current)){}} public void unlock (){Thread current = Thread.currentThread();sign .compareAndSet(current, null);}}</pre>

使用了CAS原子操作减宣,lock函數(shù)將owner設(shè)置為當(dāng)前線程,并且預(yù)測(cè)原來(lái)的值為空玩荠。unlock函數(shù)將owner設(shè)置為null漆腌,并且預(yù)測(cè)值為當(dāng)前線程。

當(dāng)有第二個(gè)線程調(diào)用lock操作時(shí)由于owner值不為空阶冈,導(dǎo)致循環(huán)一直被執(zhí)行闷尿,直至第一個(gè)線程調(diào)用unlock函數(shù)將owner設(shè)置為null,第二個(gè)線程才能進(jìn)入臨界區(qū)女坑。

由于自旋鎖只是將當(dāng)前線程不停地執(zhí)行循環(huán)體填具,不進(jìn)行線程狀態(tài)的改變,所以響應(yīng)速度更快。但當(dāng)線程數(shù)不停增加時(shí)劳景,性能下降明顯誉简,因?yàn)槊總€(gè)線程都需要執(zhí)行,占用CPU時(shí)間盟广。如果線程競(jìng)爭(zhēng)不激烈闷串,并且保持鎖的時(shí)間段。適合使用自旋鎖筋量。

鎖的優(yōu)缺點(diǎn)對(duì)比:


鎖.png

分享一個(gè)多線程并發(fā)的學(xué)習(xí)思維導(dǎo)圖:進(jìn)群619881427可以免費(fèi)獲取大量架構(gòu)師學(xué)習(xí)資料


image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末烹吵,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子桨武,更是在濱河造成了極大的恐慌肋拔,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件呀酸,死亡現(xiàn)場(chǎng)離奇詭異凉蜂,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)七咧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門跃惫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)叮叹,“玉大人艾栋,你說(shuō)我怎么就攤上這事◎韧纾” “怎么了蝗砾?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)携冤。 經(jīng)常有香客問(wèn)我悼粮,道長(zhǎng),這世上最難降的妖魔是什么曾棕? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任扣猫,我火速辦了婚禮,結(jié)果婚禮上翘地,老公的妹妹穿的比我還像新娘申尤。我一直安慰自己,他們只是感情好衙耕,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布昧穿。 她就那樣靜靜地躺著,像睡著了一般橙喘。 火紅的嫁衣襯著肌膚如雪时鸵。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天厅瞎,我揣著相機(jī)與錄音饰潜,去河邊找鬼初坠。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的梁钾。 我是一名探鬼主播庸毫,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼南誊!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起蜜托,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤抄囚,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后橄务,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體幔托,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年蜂挪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了重挑。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡棠涮,死狀恐怖谬哀,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情严肪,我是刑警寧澤史煎,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站驳糯,受9級(jí)特大地震影響篇梭,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜酝枢,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一恬偷、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧帘睦,春花似錦袍患、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至卑笨,卻和暖如春孕暇,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工妖滔, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留隧哮,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓座舍,卻偏偏與公主長(zhǎng)得像沮翔,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子曲秉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

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