一文搞懂synchronized原理

原理

衆所周知 synchronized 關鍵字是解決併發問題經常使用解決方案,有如下三種使用方式:java

  • 同步普通方法,鎖的是當前對象。
  • 同步靜態方法,鎖的是當前 Class 對象。
  • 同步塊,鎖的是 () 中的對象。

實現原理: JVM 是經過進入、退出對象監視器( Monitor )來實現對方法、同步塊的同步的。
具體實現是在編譯以後在同步方法調用前加入一個 monitor.enter 指令,在退出方法和異常處插入 monitor.exit 的指令。
其本質就是對一個對象監視器( Monitor )進行獲取,而這個獲取過程具備排他性從而達到了同一時刻只能一個線程訪問的目的。
而對於沒有獲取到鎖的線程將會阻塞到方法入口處,直到獲取鎖的線程 monitor.exit 以後才能嘗試繼續獲取鎖。安全

經過一段代碼來演示:多線程

使用 javap -c Synchronize 能夠查看編譯以後的具體信息。併發

這裏插入一下,推薦一個在IDEA中快速使用Javap的方法。性能

步驟優化

使用方法

添加以後選中類而後運行,就會把字節碼打印到控制檯,很是方便線程

鎖優化

synchronized 不少都稱之爲重量鎖,JDK1.6 中對 synchronized 進行了各類優化,爲了能減小獲取和釋放鎖帶來的消耗引入了偏向鎖輕量鎖3d

輕量鎖

當代碼進入同步塊時,若是同步對象爲無鎖狀態時,當前線程會在棧幀中建立一個鎖記錄(Lock Record)區域,同時將鎖對象的對象頭中 Mark Word 拷貝到鎖記錄中,再嘗試使用 CASMark Word 更新爲指向鎖記錄的指針。指針

若是更新成功,當前線程就得到了鎖。code

若是更新失敗 JVM 會先檢查鎖對象的 Mark Word 是否指向當前線程的鎖記錄。

若是是則說明當前線程擁有鎖對象的鎖,能夠直接進入同步塊。

不是則說明有其餘線程搶佔了鎖,若是存在多個線程同時競爭一把鎖,輕量鎖就會膨脹爲重量鎖

解鎖

輕量鎖的解鎖過程也是利用 CAS 來實現的,會嘗試鎖記錄替換回鎖對象的 Mark Word 。若是替換成功則說明整個同步操做完成,失敗則說明有其餘線程嘗試獲取鎖,這時就會喚醒被掛起的線程(此時已經膨脹爲重量鎖)

輕量鎖能提高性能的緣由是:

認爲大多數鎖在整個同步週期都不存在競爭,因此使用 CAS 比使用互斥開銷更少。但若是鎖競爭激烈,輕量鎖就不但有互斥的開銷,還有 CAS 的開銷,甚至比重量鎖更慢。

偏向鎖

爲了進一步的下降獲取鎖的代價,JDK1.6 以後還引入了偏向鎖。

偏向鎖的特徵是:鎖不存在多線程競爭,而且應由一個線程屢次得到鎖。

當線程訪問同步塊時,會使用 CAS 將線程 ID 更新到鎖對象的 Mark Word 中,若是更新成功則得到偏向鎖,而且以後每次進入這個對象鎖相關的同步塊時都不須要再次獲取鎖了。

釋放鎖

當有另一個線程獲取這個鎖時,持有偏向鎖的線程就會釋放鎖,釋放時會等待全局安全點(這一時刻沒有字節碼運行),接着會暫停擁有偏向鎖的線程,根據鎖對象目前是否被鎖來斷定將對象頭中的 Mark Word 設置爲無鎖或者是輕量鎖狀態。

偏向鎖能夠提升帶有同步卻沒有競爭的程序性能,但若是程序中大多數鎖都存在競爭時,那偏向鎖就起不到太大做用。可使用 -XX:-UseBiasedLocking 來關閉偏向鎖,並默認進入輕量鎖。

其餘優化

適應性自旋

在使用 CAS 時,若是操做失敗,CAS 會自旋再次嘗試。因爲自旋是須要消耗 CPU 資源的,因此若是長期自旋就白白浪費了 CPUJDK1.6加入了適應性自旋:

若是某個鎖自旋不多成功得到,那麼下一次就會減小自旋。

原文地址:https://crossoverjie.top/JCSprout/#/thread/Synchronize

相關文章
相關標籤/搜索