第16講 | synchronized底層如何實現?什麼是鎖的升級、降級?
synchronized代碼塊是由一對兒monitorenter/monitorexit指令實現的, Monitor對象是同步的基本實現單元。
Java 6之前, Monitor的實現完全是依靠操作系統內部的互斥鎖,因爲需要進行用戶態到內核態的切換,所以同步操作是一個無差別的重量級操作.
現代的(Oracle) JDK中, JVM對此進行了大刀闊斧地改進,提供了三種不同的Monitor實現,也就是常說的三種不同的鎖:
- 偏斜鎖(Biased Locking)
- 輕量級鎖
- 重量級鎖
大大改進了其性能。
所謂鎖的升級、降級,就是JVM優化synchronized運行的機制,當JVM檢測到不同的競爭狀況時,會自動切換到適合的鎖實現,這種切換就是鎖的升級、降級。
當沒有競爭出現時,默認會使用偏斜鎖。 JVM會利用CAS操作(compare and swap),在對象頭上的Mark Word部分設置線程ID,以表示這個對象偏向於當前線程,所以並不涉及真正的互斥鎖。這樣做的假設是基於在很多應用場景中,大部分對象生命週期中最多會被一個線程鎖定,使用偏斜鎖可以降低無競爭開銷。
如果有另外的線程試圖鎖定某個已經被偏斜過的對象, JVM就需要撤銷(revoke)偏斜鎖,並切換到輕量級鎖實現。輕量級鎖依賴CAS操作Mark Word來試圖獲取鎖,如果重試成
功,就使用普通的輕量級鎖;否則,進一步升級爲重量級鎖。
極客時間 :《Java核心技術面試精講》
自我理解
synchronized是由一對兒monitorenter/monitorexit指令實現。
不可不說的Java“鎖”事
流程圖:
阻塞或喚醒一個Java線程需要操作系統切換CPU狀態來完成,這種狀態轉換需要耗費處理器時間。如果同步代碼塊中的內容過於簡單,狀態轉換消耗的時間有可能比用戶代碼執行的時間還要長。
在許多場景中,同步資源的鎖定時間很短,爲了這一小段時間去切換線程,線程掛起和恢復現場的花費可能會讓系統得不償失。如果物理機器有多個處理器,能夠讓兩個或以上的線程同時並行執行,我們就可以讓後面那個請求鎖的線程不放棄CPU的執行時間,看看持有鎖的線程是否很快就會釋放鎖。
而爲了讓當前線程“稍等一下”,我們需讓當前線程進行自旋,如果在自旋完成後前面鎖定同步資源的線程已經釋放了鎖,那麼當前線程就可以不必阻塞而是直接獲取同步資源,從而避免切換線程的開銷。這就是自旋鎖。
java對象
存放在堆內存中
對象大致可以分爲三個部分
- 對象頭
- Mark Word(標記字段)
- Klass Pointer(類型指針)
- 實例變量
- 填充字節
Synchronized鎖對象是存在鎖對象的對象頭的MarkWord中。
鎖狀態 | 存儲內容 | 存儲內容 |
---|---|---|
無鎖 | 對象的hashCode、對象分代年齡、是否是偏向鎖(0) | 01 |
偏向鎖 | 偏向線程ID、偏向時間戳、對象分代年齡、是否是偏向鎖(1) | 01 |
輕量級鎖 | 指向棧中鎖記錄的指針 | 00 |
重量級鎖 | 指向互斥量(重量級鎖)的指針 | 10 |
逐級遞增過程:
參考:
極客時間:《Java核心技術面試精講》
本筆記根據專欄主題進行學習筆記,雖然參考了許多做了筆記,但是加上了自己的整理,跟原作者的行文可能有很大偏差。如果想查看原版請自行搜索。謝謝