【java併發程式設計實戰4】偏向鎖-輕量鎖-重量鎖的那點祕密(synchronize實現原理)
目錄
【SpringBoot2.0文章彙總目錄,java多執行緒教程文章彙總 長期更新系列
】
請多多支援
在多執行緒併發程式設計中,synchronized一直都是元老級別的角色,人們都通常稱呼它為重量鎖,但是在jdk1.6版本之後,jdk就對synchronized做了大量的優化,這時候我們就不能稱呼它為重量鎖了,有的時候它也是很輕的,那麼接下來我們就調調,synchronized是怎麼被優化的,它跟偏向鎖、輕量鎖、重量鎖又有什麼淵源。
synchronized
回顧一下synchronized是怎麼使用的呢。
1、同步普通方法
public synchronized void sync1() {
// do somethings
}
在該方法中,synchronized鎖的是當前例項的物件
2、同步靜態方法
public static synchronized void sync2() {
// do somethings
}
由於該方法是一個靜態方法,那麼它鎖的當前類的class物件。
3、同步方法塊
public void sync3() {
synchronized(this) {
// do somethings
}
}
public void sync4() {
synchronized(MyTest.css) {
// do somethings
}
}
那麼同步方法塊是需要根據方法中具體同步的物件來實現的。
在上面程式碼中其實sync3()
跟同步普通方法一樣,鎖的是當前例項物件;那麼sync4
方法就與同步靜態方法一樣,鎖的是當前類的class物件。
從上面程式碼可以看出來的,我們通過使用synchronized
關鍵字可以很簡單的解決併發問題,但是其實是jvm底層通過使用一種叫內建鎖的手段,簡化了開發人員實現併發的複雜度,在jdk1.6以前 synchronized是基於重量鎖實現的,即每次遇到同步程式碼都要獲取鎖,然後釋放鎖,在jdk1.6之後對其優化,根據不同場景使用不同的策略,這也就是 偏向鎖、輕量鎖、重量鎖的來由。在介紹他們之前我先介紹一下另一個鎖-自旋鎖。聽到這麼多鎖,是不是頭暈,當初我學習的時候也是這樣的。但是當你慢慢學習深入,你就會很容易的理解每個鎖的作用。
自旋鎖
自旋鎖顧名思義,就是自己旋轉轉圈等待,那麼它有什麼作用呢?
- 當前執行緒嘗試去競爭鎖
- 競爭失敗,準備阻塞自己
- 但是並沒有阻塞自己,而是採用自旋鎖,進入自旋狀態
- 進入自旋狀態,並且重新不斷競爭鎖
- 如果在自旋期間成功獲取鎖,那麼結束自旋狀態,否則進入阻塞狀態
如果在自旋期間成功獲取鎖,那麼就減少一次執行緒的切換。
根據上面解釋我們可以很容易的明白自旋鎖的意義,因為cpu從核心態切換至使用者態,執行緒的阻塞與恢復會浪費資源的,但是通過自旋而不是去阻塞當前執行緒,那麼就會節省這個一個cpu狀態切換。
所以自旋鎖適合在** 持有鎖的時間長,且競爭不激烈**的場景下使用。
使用-XX:-UseSpinning引數關閉自旋鎖優化;-XX:PreBlockSpin引數修改預設的自旋次數
偏向鎖
在實際場景中,如果一個同步方法,沒有多執行緒競爭,並且總是由同一個執行緒多次獲取鎖,如果每次還有阻塞執行緒,喚醒cpu從使用者態轉核心態,那麼對於cpu是一種資源的浪費,為了解決這類問題,舊引入了偏向鎖的概念。
“偏向”的意思是,偏向鎖假定將來只有第一個申請鎖的執行緒會使用鎖(不會有任何執行緒再來申請鎖),因此,只需要在Mark Word中CAS記錄owner(本質上也是更新,但初始值為空),如果記錄成功,則偏向鎖獲取成功,記錄鎖狀態為偏向鎖,以後當前執行緒等於owner就可以零成本的直接獲得鎖;否則,說明有其他執行緒競爭,膨脹為輕量級鎖。
具體的步驟如下
訪問同步程式碼塊
檢查物件頭是否owner是否儲存當前現成的id
如果沒有,進行CAS嘗試替換mark word中的owner 如果有執行同步程式碼(代表獲取鎖成功)
修改成功 (代表無競爭)owner修改為當前執行緒id,執行同步程式碼 修改失敗(代表有競爭) 進入撤銷偏向鎖,暫停執行緒並將owner置空,進入輕量鎖。
偏向鎖無法使用自旋鎖優化,因為一旦有其他執行緒申請鎖,就破壞了偏向鎖的假定。
如果你確定應用程式中所有的鎖通常是在競爭狀態,你可以通過JVM引數關閉偏向鎖
UseBiasedLocking = false,那麼程式會預設進入輕量鎖狀態。
輕量鎖
如果說偏向鎖是為了解決同步程式碼在單執行緒下訪問效能問題,那麼輕量鎖是為了解決減少無實際競爭情況下,使用重量級鎖產生的效能消耗
輕量鎖,顧名思義,輕量是相對於重量的問題,使用輕量鎖時,不需要申請互斥量(mutex)
,而是將mark word中的資訊複製到當前執行緒的棧中,然後通過cas嘗試修改mark word並替換成輕量鎖,如果替換成功則執行同步程式碼。如果此時有執行緒2來競爭,並且他也嘗試cas修改mark word但是失敗了,那麼執行緒2會進入自旋狀態,如果在自旋狀態也沒有修改成功,那麼輕量鎖將膨脹成狀態,mark word會被修改成重量鎖標記(10) ,執行緒進入阻塞狀態。
當然,由於輕量級鎖天然瞄準不存在鎖競爭的場景,如果存在鎖競爭但不激烈,仍然可以用自旋鎖優化,自旋失敗後再膨脹為重量級鎖。
重量鎖
在jvm規範中,synchronized是基於監視器鎖(monitor)來實現的,它會在同步程式碼之前新增一個monitorenter
指令,獲取到該物件的monitor,同時它會在同步程式碼結束處和異常處新增一個monitorexit
指令去釋放該物件的monitor,需要注意的是每一個物件都有一個monitor與之配對,當一個monitor被獲取之後 也就是被monitorenter
,它會處於一個鎖定狀態,其他嘗試獲取該物件的monitor的執行緒會獲取失敗,只有當獲取該物件的monitor的執行緒執行了monitorexit
指令後,其他執行緒才有可能獲取該物件的monitor成功。
所以從上面描述可以得出,監視器鎖就是monitor
它是互斥的(mutex)。由於它是互斥的,那麼它的操作成本就非常的高,包括系統呼叫引起的核心態與使用者態切換、執行緒阻塞造成的執行緒切換等。因此,後來稱這種鎖為“重量級鎖”。
小結
偏向鎖、輕量級鎖、重量級鎖適用於不同的併發場景:
- 偏向鎖:無實際競爭,且將來只有第一個申請鎖的執行緒會使用鎖。
- 輕量級鎖:無實際競爭,多個執行緒交替使用鎖;允許短時間的鎖競爭。
- 重量級鎖:有實際競爭,且鎖競爭時間長。
另外,如果鎖競爭時間短,可以使用自旋鎖進一步優化輕量級鎖、重量級鎖的效能,減少執行緒切換。
如果鎖競爭程度逐漸提高(緩慢),那麼從偏向鎖逐步膨脹到重量鎖,能夠提高系統的整體效能。
同時需要注意鎖可以升級,但是不能降級。
另外通過這次學習,大家應該也知道自從jdk1.6以後 synchronized
已經被優化了,效能不會比Lock
差
所以jdk.16版本及其以後版本的同學可以放心大膽的使用了。
最後附一張從偏向鎖膨脹至重量鎖的完全的流程圖
最後歡迎大家關注一下我的個人公眾號。一起交流一起學習,有問必答。
相關文章
- java 中的鎖 -- 偏向鎖、輕量級鎖、自旋鎖、重量級鎖Java
- java併發筆記之synchronized 偏向鎖 輕量級鎖 重量級鎖證明Java筆記synchronized
- 淺談偏向鎖、輕量級鎖、重量級鎖
- 深入理解偏向鎖、輕量級鎖、重量級鎖
- Java併發程式設計:Synchronized底層優化(偏向鎖、輕量級鎖)Java程式設計synchronized優化
- Java併發程式設計實戰(4)- 死鎖Java程式設計
- JAVA物件分析之偏向鎖、輕量級鎖、重量級鎖升級過程Java物件
- 【JavaSE】淺談偏向鎖、輕量級鎖和重量級鎖,如何獲取鎖,如何撤銷鎖。Java
- 《JAVA併發程式設計實戰》顯式鎖Java程式設計
- Java併發程式設計實戰--閉鎖 CountDownLatchJava程式設計CountDownLatch
- 併發程式設計實戰——鎖分段程式設計
- 自旋鎖、阻塞鎖、可重入鎖、悲觀鎖、樂觀鎖、讀寫鎖、偏向所、輕量級鎖、重量級鎖、鎖膨脹、物件鎖和類鎖物件
- 輕量級分散式鎖的設計原理分析與實現分散式
- Java 併發機制底層實現 —— volatile 原理、synchronize 鎖優化機制Java優化
- 偏向鎖理論太抽象,實戰了解下偏向鎖如何發生以及如何升級【實戰篇】抽象
- Java併發程式設計實戰 04死鎖了怎麼辦?Java程式設計
- Java高併發實戰,鎖的優化Java優化
- 一文讀懂原子操作、記憶體屏障、鎖(偏向鎖、輕量級鎖、重量級鎖、自旋鎖)、Disruptor、Go Context之上半部分記憶體GoContext
- Java併發4:鎖Java
- Java併發程式設計-鎖及併發容器Java程式設計
- 併發程式設計之顯式鎖原理程式設計
- Java併發基礎-鎖的使用及原理(可重入鎖、讀寫鎖、內建鎖、訊號量等)Java
- 【Java面試】為什麼引入偏向鎖、輕量級鎖,介紹下升級流程Java面試
- Java併發程式設計之鎖機制之(ReentrantLock)重入鎖Java程式設計ReentrantLock
- java中的鎖及實現原理Java
- 併發程式設計之 Java 三把鎖程式設計Java
- java程式設計思想之併發(死鎖)Java程式設計
- Java併發程式設計實戰--協作物件間的死鎖與開放呼叫Java程式設計物件
- 死磕Synchronized底層實現–偏向鎖synchronized
- Java偏向鎖淺析Java
- 《實戰 Java 高併發程式設計》筆記——第4章 鎖的優化及注意事項(二)Java程式設計筆記優化
- Java併發程式設計(05):悲觀鎖和樂觀鎖機制Java程式設計
- 執行緒安全(上)--徹底搞懂synchronized(從偏向鎖到重量級鎖執行緒synchronized
- 執行緒安全(中)--徹底搞懂synchronized(從偏向鎖到重量級鎖)執行緒synchronized
- 併發程式設計之死鎖解析程式設計
- Redis實現併發阻塞鎖方案Redis
- Java併發程式設計——深入理解自旋鎖Java程式設計
- 偏向鎖狀態轉移原理