天天看點

Java -- 每日一問:synchronized底層如何實作?什麼是鎖的更新、降級?

Java -- 每日一問:synchronized底層如何實作?什麼是鎖的更新、降級?

典型回答

在回答這個問題前,先簡單複習一下上一講的知識點。synchronized 代碼塊是由一對兒 monitorenter/monitorexit 指令實作的,Monitor 對象是同步的基本實作單元。

在 Java 6 之前,Monitor 的實作完全是依靠作業系統内部的互斥鎖,因為需要進行使用者态到核心态的切換,是以同步操作是一個無差别的重量級操作。

現代的(Oracle)JDK 中,JVM 對此進行了大刀闊斧地改進,提供了三種不同的 Monitor 實作,也就是常說的三種不同的鎖:偏斜鎖(Biased Locking)、輕量級鎖和重量級鎖,大大改進了其性能。

所謂鎖的更新、降級,就是 JVM 優化 synchronized 運作的機制,當 JVM 檢測到不同的競争狀況時,會自動切換到适合的鎖實作,這種切換就是鎖的更新、降級。

當沒有競争出現時,預設會使用偏斜鎖。JVM 會利用 CAS 操作(compare and swap),在對象頭上的 Mark Word 部分設定線程 ID,以表示這個對象偏向于目前線程,是以并不涉及真正的互斥鎖。這樣做的假設是基于在很多應用場景中,大部分對象生命周期中最多會被一個線程鎖定,使用偏斜鎖可以降低無競争開銷。

如果有另外的線程試圖鎖定某個已經被偏斜過的對象,JVM 就需要撤銷(revoke)偏斜鎖,并切換到輕量級鎖實作。輕量級鎖依賴 CAS 操作 Mark Word 來試圖擷取鎖,如果重試成功,就使用普通的輕量級鎖;否則,進一步更新為重量級鎖。

我注意到有的觀點認為 Java 不會進行鎖降級。實際上據我所知,鎖降級确實是會發生的,當 JVM 進入安全點(SafePoint)的時候,會檢查是否有閑置的 Monitor,然後試圖進行降級。

高手回答

自旋鎖:競争鎖的失敗的線程,并不會真實的在作業系統層面挂起等待,而是JVM會讓線程做幾個空循環(基于預測在不久的将來就能獲得),在經過若幹次循環後,如果可以獲得鎖,那麼進入臨界區,如果還不能獲得鎖,才會真實的将線程在作業系統層面進行挂起。