天天看點

【Java技術探索】深入了解synchronized關鍵字原理(上)

synchronized同步關鍵字簡介

synchronized是屬于JVM層面的一個關鍵字,底層是通過一個monitor對象(管程對象)來完成,由于wait()/notify()等方法也依賴于monitor對象,是以隻有在同步的塊或者方法中才能調用wait/notify等方法
【Java技術探索】深入了解synchronized關鍵字原理(上)
【Java技術探索】深入了解synchronized關鍵字原理(上)

synchronized同步代碼塊底層實作

  • synchronized同步語句塊的實作使用monitorenter和 monitorexit 指令。
    • monitorenter:指令指向同步代碼塊的開始位置
    • monitorexit:指令則指明同步代碼塊的結束位置

synchronized擷取鎖

  • 當執行monitorenter指令時,目前線程将試圖擷取 objectref(即對象鎖) 所對應的 monitor的持有權,當 objectref的monitor 的進入計數器**為0,那線程可以成功取得 monitor,并将計數器值設定為 1,取鎖成功。
  • 如果目前線程已經擁有 objectref 的 monitor 的持有權,那它可以重入這個 monitor (關于重入性稍後會分析),重入時計數器的值也會加 1。
  • 倘若其他線程已經擁有objectref 的 monitor 的所有權,那目前線程将被阻塞,直到正在執行線程執行完畢,即monitorexit指令被執行,執行線程将釋放monitor(鎖)并設定計數器值為0,其他線程将有機會持有 monitor ;**

為了保證在方法異常完成時 monitorenter 和 monitorexit 指令依然可以正确配對執行,編譯器會自動産生一個異常處理器,這個異常處理器聲明可處理所有的異常,它的目的就是用來執行 monitorexit 指令。

從位元組碼中也可以看出多了一個monitorexit指令,它就是異常結束時被執行的釋放monitor的指令;

monitorenter(進入管程對象)

每個對象有一個管程對象(monitor),當monitor被占用時就會處于鎖定狀态;線程執行monitorenter指令時嘗試擷取monitor的所有權,過程如下:
  1. 如果monitor的進入計數器為0,則該線程進入monitor,然後将進入計數器設定為1,該線程即為monitor的所有者;
  2. 如果線程已經之前占用了該monitor,本次隻是重新進入,則将monitor的進入計數器加1;
  3. 如果其他線程已經占用了monitor,則該線程進入阻塞狀态,直到monitor的進入計數器為0,再重新嘗試擷取monitor的所有權;

monitorexit(退出管程對象)

  • 執行monitorexit的線程必須是objectref(即對象鎖)所對應的monitor的所有者;
  • 指令執行時,monitor的進入計數器減1,如果減1後進入計數器為0,那線程退出monitor,不再是這個monitor的所有者。其他被這個monitor阻塞的線程可以嘗試去擷取這個 monitor 的所有權.

同步方法中synchronized底層實作

方法級的同步是隐式,即無需通過位元組碼指令(monitorenter和monitorexit)來控制的,它實作在方法調用和傳回操作之中。
【Java技術探索】深入了解synchronized關鍵字原理(上)
【Java技術探索】深入了解synchronized關鍵字原理(上)
  • 然後再執行方法,最後在方法完成(無論是正常完成還是非正常完成)時釋放monitor。