目錄
【SpringBoot2.0文章匯總目錄沐批,java多線程教程文章匯總 長(zhǎng)期更新系列
】
請(qǐng)多多支持
在多線程并發(fā)編程中,synchronized一直都是元老級(jí)別的角色,人們都通常稱呼它為重量鎖,但是在jdk1.6版本之后,jdk就對(duì)synchronized做了大量的優(yōu)化凳怨,這時(shí)候我們就不能稱呼它為重量鎖了,有的時(shí)候它也是很輕的是鬼,那么接下來(lái)我們就調(diào)調(diào)肤舞,synchronized是怎么被優(yōu)化的,它跟偏向鎖屑咳、輕量鎖萨赁、重量鎖又有什么淵源。
synchronized
回顧一下synchronized是怎么使用的呢兆龙。
1杖爽、同步普通方法
public synchronized void sync1() {
// do somethings
}
在該方法中,synchronized鎖的是當(dāng)前實(shí)例的對(duì)象
2紫皇、同步靜態(tài)方法
public static synchronized void sync2() {
// do somethings
}
由于該方法是一個(gè)靜態(tài)方法慰安,那么它鎖的當(dāng)前類的class對(duì)象。
3聪铺、同步方法塊
public void sync3() {
synchronized(this) {
// do somethings
}
}
public void sync4() {
synchronized(MyTest.css) {
// do somethings
}
}
那么同步方法塊是需要根據(jù)方法中具體同步的對(duì)象來(lái)實(shí)現(xiàn)的化焕。
在上面代碼中其實(shí)sync3()
跟同步普通方法一樣,鎖的是當(dāng)前實(shí)例對(duì)象铃剔;那么sync4
方法就與同步靜態(tài)方法一樣撒桨,鎖的是當(dāng)前類的class對(duì)象查刻。
從上面代碼可以看出來(lái)的,我們通過(guò)使用synchronized
關(guān)鍵字可以很簡(jiǎn)單的解決并發(fā)問(wèn)題凤类,但是其實(shí)是jvm底層通過(guò)使用一種叫內(nèi)置鎖的手段穗泵,簡(jiǎn)化了開(kāi)發(fā)人員實(shí)現(xiàn)并發(fā)的復(fù)雜度,在jdk1.6以前 synchronized是基于重量鎖實(shí)現(xiàn)的谜疤,即每次遇到同步代碼都要獲取鎖佃延,然后釋放鎖,在jdk1.6之后對(duì)其優(yōu)化夷磕,根據(jù)不同場(chǎng)景使用不同的策略履肃,這也就是 偏向鎖、輕量鎖坐桩、重量鎖的來(lái)由尺棋。在介紹他們之前我先介紹一下另一個(gè)鎖-自旋鎖。聽(tīng)到這么多鎖撕攒,是不是頭暈陡鹃,當(dāng)初我學(xué)習(xí)的時(shí)候也是這樣的。但是當(dāng)你慢慢學(xué)習(xí)深入抖坪,你就會(huì)很容易的理解每個(gè)鎖的作用。
自旋鎖
自旋鎖顧名思義闷叉,就是自己旋轉(zhuǎn)轉(zhuǎn)圈等待擦俐,那么它有什么作用呢?
- 當(dāng)前線程嘗試去競(jìng)爭(zhēng)鎖
- 競(jìng)爭(zhēng)失敗握侧,準(zhǔn)備阻塞自己
- 但是并沒(méi)有阻塞自己蚯瞧,而是采用自旋鎖,進(jìn)入自旋狀態(tài)
- 進(jìn)入自旋狀態(tài)品擎,并且重新不斷競(jìng)爭(zhēng)鎖
- 如果在自旋期間成功獲取鎖埋合,那么結(jié)束自旋狀態(tài),否則進(jìn)入阻塞狀態(tài)
如果在自旋期間成功獲取鎖萄传,那么就減少一次線程的切換甚颂。
根據(jù)上面解釋我們可以很容易的明白自旋鎖的意義,因?yàn)閏pu從內(nèi)核態(tài)切換至用戶態(tài)秀菱,線程的阻塞與恢復(fù)會(huì)浪費(fèi)資源的振诬,但是通過(guò)自旋而不是去阻塞當(dāng)前線程,那么就會(huì)節(jié)省這個(gè)一個(gè)cpu狀態(tài)切換衍菱。
所以自旋鎖適合在** 持有鎖的時(shí)間長(zhǎng)赶么,且競(jìng)爭(zhēng)不激烈**的場(chǎng)景下使用。
使用-XX:-UseSpinning參數(shù)關(guān)閉自旋鎖優(yōu)化脊串;-XX:PreBlockSpin參數(shù)修改默認(rèn)的自旋次數(shù)
偏向鎖
在實(shí)際場(chǎng)景中辫呻,如果一個(gè)同步方法清钥,沒(méi)有多線程競(jìng)爭(zhēng),并且總是由同一個(gè)線程多次獲取鎖放闺,如果每次還有阻塞線程祟昭,喚醒cpu從用戶態(tài)轉(zhuǎn)核心態(tài),那么對(duì)于cpu是一種資源的浪費(fèi)雄人,為了解決這類問(wèn)題从橘,舊引入了偏向鎖的概念。
“偏向”的意思是础钠,偏向鎖假定將來(lái)只有第一個(gè)申請(qǐng)鎖的線程會(huì)使用鎖(不會(huì)有任何線程再來(lái)申請(qǐng)鎖)恰力,因此,只需要在Mark Word中CAS記錄owner(本質(zhì)上也是更新旗吁,但初始值為空)踩萎,如果記錄成功,則偏向鎖獲取成功很钓,記錄鎖狀態(tài)為偏向鎖香府,以后當(dāng)前線程等于owner就可以零成本的直接獲得鎖;否則码倦,說(shuō)明有其他線程競(jìng)爭(zhēng)企孩,膨脹為輕量級(jí)鎖。
具體的步驟如下
訪問(wèn)同步代碼塊
檢查對(duì)象頭是否owner是否存儲(chǔ)當(dāng)前現(xiàn)成的id
如果沒(méi)有袁稽,進(jìn)行CAS嘗試替換mark word中的owner 如果有執(zhí)行同步代碼(代表獲取鎖成功)
修改成功 (代表無(wú)競(jìng)爭(zhēng))owner修改為當(dāng)前線程id,執(zhí)行同步代碼 修改失敗(代表有競(jìng)爭(zhēng)) 進(jìn)入撤銷偏向鎖勿璃,暫停線程并將owner置空,進(jìn)入輕量鎖推汽。
偏向鎖無(wú)法使用自旋鎖優(yōu)化补疑,因?yàn)橐坏┯衅渌€程申請(qǐng)鎖,就破壞了偏向鎖的假定歹撒。
如果你確定應(yīng)用程序中所有的鎖通常是在競(jìng)爭(zhēng)狀態(tài)莲组,你可以通過(guò)JVM參數(shù)關(guān)閉偏向鎖
UseBiasedLocking = false,那么程序會(huì)默認(rèn)進(jìn)入輕量鎖狀態(tài)暖夭。
輕量鎖
如果說(shuō)偏向鎖是為了解決同步代碼在單線程下訪問(wèn)性能問(wèn)題锹杈,那么輕量鎖是為了解決減少無(wú)實(shí)際競(jìng)爭(zhēng)情況下,使用重量級(jí)鎖產(chǎn)生的性能消耗
輕量鎖鳞尔,顧名思義嬉橙,輕量是相對(duì)于重量的問(wèn)題,使用輕量鎖時(shí)寥假,不需要申請(qǐng)互斥量(mutex)
,而是將mark word中的信息復(fù)制到當(dāng)前線程的棧中市框,然后通過(guò)cas嘗試修改mark word并替換成輕量鎖,如果替換成功則執(zhí)行同步代碼糕韧。如果此時(shí)有線程2來(lái)競(jìng)爭(zhēng)枫振,并且他也嘗試cas修改mark word但是失敗了喻圃,那么線程2會(huì)進(jìn)入自旋狀態(tài),如果在自旋狀態(tài)也沒(méi)有修改成功粪滤,那么輕量鎖將膨脹成狀態(tài)斧拍,mark word會(huì)被修改成重量鎖標(biāo)記(10) ,線程進(jìn)入阻塞狀態(tài)。
當(dāng)然杖小,由于輕量級(jí)鎖天然瞄準(zhǔn)不存在鎖競(jìng)爭(zhēng)的場(chǎng)景肆汹,如果存在鎖競(jìng)爭(zhēng)但不激烈,仍然可以用自旋鎖優(yōu)化予权,自旋失敗后再膨脹為重量級(jí)鎖昂勉。
重量鎖
在jvm規(guī)范中,synchronized是基于監(jiān)視器鎖(monitor)來(lái)實(shí)現(xiàn)的扫腺,它會(huì)在同步代碼之前添加一個(gè)monitorenter
指令岗照,獲取到該對(duì)象的monitor,同時(shí)它會(huì)在同步代碼結(jié)束處和異常處添加一個(gè)monitorexit
指令去釋放該對(duì)象的monitor,需要注意的是每一個(gè)對(duì)象都有一個(gè)monitor與之配對(duì)笆环,當(dāng)一個(gè)monitor被獲取之后 也就是被monitorenter
攒至,它會(huì)處于一個(gè)鎖定狀態(tài),其他嘗試獲取該對(duì)象的monitor的線程會(huì)獲取失敗躁劣,只有當(dāng)獲取該對(duì)象的monitor的線程執(zhí)行了monitorexit
指令后迫吐,其他線程才有可能獲取該對(duì)象的monitor成功。
所以從上面描述可以得出账忘,監(jiān)視器鎖就是monitor
它是互斥的(mutex)渠抹。由于它是互斥的,那么它的操作成本就非常的高闪萄,包括系統(tǒng)調(diào)用引起的內(nèi)核態(tài)與用戶態(tài)切換、線程阻塞造成的線程切換等奇颠。因此败去,后來(lái)稱這種鎖為“重量級(jí)鎖”。
小結(jié)
偏向鎖烈拒、輕量級(jí)鎖圆裕、重量級(jí)鎖適用于不同的并發(fā)場(chǎng)景:
- 偏向鎖:無(wú)實(shí)際競(jìng)爭(zhēng),且將來(lái)只有第一個(gè)申請(qǐng)鎖的線程會(huì)使用鎖荆几。
- 輕量級(jí)鎖:無(wú)實(shí)際競(jìng)爭(zhēng)吓妆,多個(gè)線程交替使用鎖;允許短時(shí)間的鎖競(jìng)爭(zhēng)吨铸。
- 重量級(jí)鎖:有實(shí)際競(jìng)爭(zhēng)行拢,且鎖競(jìng)爭(zhēng)時(shí)間長(zhǎng)。
另外诞吱,如果鎖競(jìng)爭(zhēng)時(shí)間短舟奠,可以使用自旋鎖進(jìn)一步優(yōu)化輕量級(jí)鎖竭缝、重量級(jí)鎖的性能,減少線程切換沼瘫。
如果鎖競(jìng)爭(zhēng)程度逐漸提高(緩慢)抬纸,那么從偏向鎖逐步膨脹到重量鎖,能夠提高系統(tǒng)的整體性能耿戚。
同時(shí)需要注意鎖可以升級(jí)湿故,但是不能降級(jí)。
另外通過(guò)這次學(xué)習(xí)膜蛔,大家應(yīng)該也知道自從jdk1.6以后 synchronized
已經(jīng)被優(yōu)化了坛猪,性能不會(huì)比Lock
差
所以jdk.16版本及其以后版本的同學(xué)可以放心大膽的使用了。
最后附一張從偏向鎖膨脹至重量鎖的完全的流程圖
最后歡迎大家關(guān)注一下我的個(gè)人公眾號(hào)飞几。一起交流一起學(xué)習(xí)砚哆,有問(wèn)必答。