作者:duktig
部落格:https://duktig.cn
優秀還努力。願你付出甘之如饴,所得歸于歡喜。
背景
synchronized
在多線程和高并發中經常用到,尤其是
synchronized
在JDK6之後進行了優化,效率堪比Lock鎖,甚至猶有過之。也是面試中的常客。
面試官:你平時用過多線程嗎?
面試官:你怎麼解決線程安全問題呢?
我:最常用的就是讓多線程的操作同步處理,常用的有
鎖和
Lock
面試官:那你能說說
synchronized
鎖和
Lock
synchronized
的差別嗎?
我:(心裡想,這個比較容易)……
面試官:你能和所說
synchronized
的原理嗎?
我:(這個之前剛好研究過)……
面試官:
synchronized
以前是重量級鎖,之後有了很多優化,你能說說嗎?
我:
synchronized
在JDK6有了很多優化,最重要的有個鎖更新的過程,提高了獲得鎖和釋放鎖的過程。
面試官:那你能說說
synchronized
鎖更新的過程嗎?以及它是怎麼進行辨別的?
我:(GG,這個之前看了很多次,但是都沒理清楚)…… (說的一塌糊塗)
本篇文章就以清晰的流程來闡述
synchronized
的鎖更新過程,以及是怎麼進行辨別的。
synchronized與對象頭
synchronized用的鎖是存在Java對象頭裡的。如果對象是數組類型,則虛拟機用3個字寬(Word)存儲對象頭,如果對象是非數組類型,則用2字寬存儲對象頭。
Java對象頭的長度:
Java對象頭裡的Mark Word裡預設存儲對象的HashCode、分代年齡和鎖标記位。
Java對象頭的存儲結構:
在運作期間,Mark Word裡存儲的資料會随着鎖标志位的變化而變化。Mark Word可能變化為存儲以下4種資料:
32位Mark Word的狀态變化:
在64位虛拟機下,Mark Word是64bit大小的,其存儲結構如表:
synchronized的鎖更新過程
鎖更新過程依次為:無鎖狀态、偏向鎖、輕量級鎖、重量級鎖。
synchronized可以進行鎖更新,但是不能降級,目的是為了提高獲得鎖和釋放鎖的效率。
偏向鎖
HotSpot作者發現,大多數情況下,鎖不僅不存在多線程競争,而且總是由同一線程多次獲得,為了讓線程獲得鎖的代價更低而引入了偏向鎖。
對象頭:
- 23bit的線程ID;
- 1bit是否為偏向鎖值為1說明是偏向鎖狀态;
- 無鎖和偏向鎖2bit的鎖标志位都是01
偏向鎖加鎖過程:
- 檢查對象頭和棧幀中的鎖記錄是否存儲了目前線程的線程ID
- 即簡單測試一下對象頭的Mark Word裡是否存儲着指向目前線程的偏向鎖即可
- (為了保證目前線程每次進入和退出同步塊時不需要CAS操作加鎖和解鎖)
- 測試成功表示獲得了鎖
- 測試失敗再測試一下Mark Word中偏向鎖的辨別是否設定成1(表示目前是偏向鎖)
- 如果沒有設定,則使用CAS競争鎖
- 如果設定了,則嘗試使用CAS将對象頭的偏向鎖指向目前線程
偏向鎖撤銷過程:
偏向鎖使用了一種等到競争出現才釋放鎖的機制,是以當其他線程嘗試競争偏向鎖時,持有偏向鎖的線程才會釋放鎖。
- 前提條件:等待全局安全點(在這個時間點上沒有正在執行的位元組碼)
- 暫停擁有偏向鎖的線程,然後檢查持有偏向鎖的線程是否活着
- 如果線程不處于活動狀态,則将對象頭設定成無鎖狀态
- 如果線程仍然活着,擁有偏向鎖的棧會被執行,周遊偏向對象的鎖記錄,棧中的鎖記錄和對象頭的Mark Word要麼重新偏向于其他線程,要麼恢複到無鎖或者标記對象不适合作為偏向鎖
- 最後喚醒暫停的線程
偏向鎖獲得和撤銷鎖的過程:
偏向鎖的更新:
隻要存在兩個線程的并發競争,偏向鎖更新為輕量級鎖。
輕量級鎖
輕量級鎖加鎖過程:
- 線程在執行同步塊前,JVM在目前線程棧幀中配置設定空間,并将對象頭中的Mark Word複制到鎖記錄中(官方稱為Displaced Mark Word)
- CAS将對象頭中的Mark Word替換為指向鎖記錄的指針
- 如果成功,目前線程獲得鎖
- 如果失敗,表示其他線程競争鎖,目前線程便嘗試使用自旋來擷取鎖
輕量級鎖撤銷過程:
- CAS操作将Displaced Mark Word替換回到對象頭
- 如果成功,則表示沒有競争發生
- 如果失敗,表示目前鎖存在競争,鎖就會膨脹成重量級鎖
輕量級鎖膨脹的流程圖:
重量級鎖
因為自旋會消耗CPU,為了避免無用的自旋(比如獲得鎖的線程被阻塞住了),一旦鎖更新成重量級鎖,就不會再恢複到輕量級鎖狀态。
當鎖處于重量級鎖狀态下,其他線程試圖擷取鎖時,都會被阻塞住,當持有鎖的線程釋放鎖之後會喚醒這些線程,被喚醒的線程就會進行新一輪的奪鎖之争。
鎖的優缺點對比
參看:
- 《Java并發程式設計的藝術》
- 《Java多線程程式設計核心技術》
- 曾經總結的多線程相關的筆記