Java併發(四)synchronized關鍵字(1)基本原理

一.用法

就記住一點:synchronized修飾非靜態方法時,鎖是this,即當前的實例對象。synchronized修飾靜態方法時,鎖是類對象。

二.原理

2.1 預備知識(對象頭)

  1. 在HotSpot虛擬機裏,對象在堆內存中的存儲佈局可以劃分爲三個部分:對象頭(Header)、實例數據(Instance Data)、對齊填充(Padding)。
  2. 對象頭又分爲Mark Word 和 Klass Word。Mark Word用於存儲對象自身的運行時數據,如:哈希碼、GC分代年齡、鎖狀態標誌、線程持有的鎖、偏向線程ID等。Klass Word爲類型指針,Java虛擬機通過這個指針來確定該對象是哪個類的實例。如圖1所示:
    在這裏插入圖片描述
    圖1

2.2 Monitor

  1. Monitor被翻譯爲監視器或管程,是操作系統提供的對象。每個Java對象都可以關聯一個Monitor對象,如果使用synchronized給對象A上鎖(重量級鎖)之後,該對象A的對象頭的Mark Word中的相應部分就被設置指向Monitor對象的指針,Mark Word中的鎖標誌位置爲10。如圖2所示:
    在這裏插入圖片描述
    圖2
  2. Monitor中分三個結構:Owner、EntryList、WaitSet。
    (1)Owner:當某一線程Thread-2執行synchronized(obj)併成功加鎖,該obj關聯的Monitor的Owner就置爲Thread-2。
    (2)EntryList:用於存放那些等待Thread-2釋放obj的線程,這些線程處於BLOCKED狀態。
    (3)WaitSet:用於存放那些之前獲得過鎖,但條件不滿足而進入WAITING狀態的線程。具體見後面的wait-notify。
    具體見圖3:
    在這裏插入圖片描述
    圖3

2.3 Monitor-字節碼角度

代碼如圖4所示:
在這裏插入圖片描述
圖4

該段代碼對應的字節碼指令如圖5所示:
在這裏插入圖片描述
圖5
從圖5中看出,15行和21行都是monitorexit指令,但爲什麼會有兩次monitorexit指令呢?因爲當順利執行完16行時,鎖已經釋放了,就直接跳到24行,方法就正常返回了。但如果6到16行發生了異常,鎖有可能沒釋放,就需要跳到19行(見圖5異常表),然後往下執行,直到21行正確執行,這樣能夠保證鎖順利釋放。

三.優化

關於偏向鎖、輕量級鎖相關,看 併發編程_原理.pdf 17~33頁的內容。