1. 程式人生 > 實用技巧 >JAVA系列:Synchronized工作原理

JAVA系列:Synchronized工作原理

synchronized的位元組碼指令

通過javap -v 來檢視對應程式碼的位元組碼指令,對於同步塊的實現使用了monitorenter和monitorexit指令,前面我 們在講JMM的時候,提到過這兩個指令,他們隱式的執行了Lock和UnLock操作,用於提供原子性保證。 monitorenter指令插入到同步程式碼塊開始的位置、monitorexit指令插入到同步程式碼塊結束位置,jvm需要保證每個monitorenter都有一個monitorexit對應。

這兩個指令,本質上都是對一個物件的監視器(monitor)進行獲取,這個過程是排他的,也就是說同一時刻只能有一個執行緒獲取到由synchronized所保護物件的監視器執行緒執行到monitorenter指令時,會嘗試獲取物件所對應的monitor所有權,也就是嘗試獲取物件的鎖;而執行 monitorexit,就是釋放monitor的所有權

實現原理

Synchronized是通過物件內部的一個叫做監視器鎖(monitor)來實現的。但是監視器鎖本質又是依賴於底層的作業系統的Mutex Lock來實現的。而作業系統實現執行緒之間的切換這就需要從使用者態轉換到核心態,這個成本非常高,狀態之間的轉換需要相對比較長的時間,這就是為什麼Synchronized效率低的原因。因此,這種依賴於作業系統Mutex Lock所實現的鎖我們稱之為“重量級鎖”。JDK中對Synchronized做的種種優化,其核心都是為了減少這種重量級鎖的使用。JDK1.6以後,為了減少獲得鎖和釋放鎖所帶來的效能消耗,提高效能,引入了“輕量級鎖”和“偏向鎖”。

為了保證在方法異常完成時 monitorenter 和 monitorexit 指令依然可以正確配對執行,編譯器會自動產生一個異常處理器,這個異常處理器宣告可處理所有的異常,它的目的就是用來執行 monitorexit 指令。從位元組碼中也可以看出多了一個monitorexit指令,它就是異常結束時被執行的釋放monitor 的指令。

從上述指令我們可以得出以下結論:

同步程式碼塊是使用monitorenter和monitorexit指令實現的,會在同步塊的區域通過監聽器物件去獲取鎖和釋放鎖,從而在位元組碼層面來控制同步scope.

同步方法和靜態同步方法依靠的是方法修飾符上的ACC_SYNCHRONIZED實現

。JVM根據該修飾符來實現方法的同步。當方法呼叫時,呼叫指令將會檢查方法的 ACC_SYNCHRONIZED 訪問標誌是否被設定,如果設定了,執行執行緒將先獲取monitor,獲取成功之後才能執行方法體,方法執行完後再釋放monitor。在方法執行期間,其他任何執行緒都無法再獲得同一個monitor物件

Java物件頭

Hotspot虛擬機器的物件頭主要包括兩部分資料:Mark Word(標記欄位)、Klass Pointer(型別指標)

Klass Point是物件指向它的類元資料的指標,虛擬機器通過這個指標來 確定這個物件是哪個類的例項;
Mark Word用於儲存物件自身的執行時資料,如雜湊碼(HashCode)、GC分代年齡、 鎖狀態標誌、執行緒持有的鎖、偏向執行緒 ID、偏向時間戳等等, 它是實現輕量級鎖和偏向鎖的關鍵.