描述一下鎖的四種狀態及升級過程?

2023-03-06 21:00:45
 

1、鎖的四種狀態

無鎖、偏向鎖、輕量級鎖、重量級鎖

2、Java物件頭描述

以下為32位元物件頭描述
在這裡插入圖片描述以下為64位元物件頭描述
在這裡插入圖片描述

3、鎖的升級過程(Synchronized加鎖/膨脹流程)

1)簡單過程如下圖

在這裡插入圖片描述

2)詳細過程

當執行緒存取同步程式碼塊時,首先判斷當前鎖狀態是否為可偏向狀態(物件頭中偏向鎖=1,鎖標誌=01)
在JDK1.6以上預設開啟,開啟後程式啟動幾秒後才會被啟用

(1)偏向鎖

如果是可偏向狀態,檢查MarkWord儲存的是否是當前執行緒ID

  • 是,獲得偏向鎖,執行同步程式碼塊
  • 不是,CAS操作競爭鎖,替換執行緒ID
    • 替換成功,MarkWord的執行緒ID設定為當前執行緒ID(執行緒複用),執行同步程式碼塊
    • 替換失敗,鎖復原,升級為輕量級鎖
      同一類物件多次復原升級達到閾值20,則偏向鎖認為,後面的鎖需要重新偏向新的執行緒(批次重偏向)
      如果閾值達到40次,則偏向鎖認為偏向鎖復原過於頻繁,後面直接使用輕量級鎖

(2)輕量級鎖

升級為輕量級鎖的情況

  • 如果不是可偏向狀態,直接升級為輕量級鎖
  • 偏向鎖復原次數過多

加鎖時,會在當前執行緒棧幀中劃出一塊空間,作為該鎖記錄,並且將鎖物件MarkWord複製到該鎖記錄中,CAS操作將MarkWord更新為該鎖記錄的指標,鎖記錄中的owner指標指向物件頭的MarkWord。

  • 更新成功,MarkWord鎖標誌位為00,表示輕量級鎖狀態
  • 更新失敗,檢查鎖物件MarkWord是否指向當前執行緒棧幀中的鎖記錄
    • 是,表示鎖重入,在當前執行緒棧幀中鎖記錄+1
    • 否,自旋等待(預設10次),等待次數達到閾值,升級為重量級鎖

(3)重量級鎖

升級為重量級鎖的情況

  • 競爭加劇,CAS自旋到一定次數升級為重量級鎖
  • 自旋執行緒數超過CPU核數的一半, 1.6之後,加入自適應自旋Adapative Self Spinning,JVM自己控制

獲取鎖成功,進入EntryList(獲取鎖的緩衝區、入口)

  • 在呼叫wait方法後,會進入等待喚醒佇列(WaitSet)等待
  • 在呼叫notify方法後,則可能進入EntryList
    獲取鎖失敗,進入一個等待拿鎖佇列(cxq)等待

具體重量級鎖加鎖過程:
1、分配⼀個ObjectMonitor物件,把MarkWord鎖標誌置為‘10’,然後MarkWord儲存指向ObjectMonitor物件的指標。ObjectMonitor物件有兩個佇列和⼀個指標,每個需要獲取鎖的執行緒都包裝成ObjectWaiter物件
2、多個執行緒同時執行同⼀段同步程式碼時,ObjectWaiter先進⼊EntryList佇列,當某個執行緒獲取到物件的monitor以後進⼊Owner區域,並把monitor中的owner變數設定為當前執行緒,同時monitor中的計數器count+1

說明:
monitor:每個Java物件都有一把鎖,稱為內部鎖或monitor鎖
owner,指向的是當前獲得執行緒的地址,用來判斷當前鎖是被哪個執行緒持有

4、拓展

1)synchronized效率低?

使用者態:偏向鎖、輕量級鎖
核心態:重量級鎖

首先來了解下synchronized重量級鎖實現原理?
  通過物件內部的一個叫做監視器鎖(monitor)來實現的,監視器鎖本質又是依賴於底層的作業系統的 Mutex Lock(互斥鎖)來實現的。而作業系統實現執行緒之間的切換需要從使用者態轉換到核心態,這個成本非常高。
  在JDK6以前,只有重量級鎖,阻塞或喚醒一個Java執行緒需要作業系統切換CPU狀態來完成,這種狀態切換需要耗費處理器時間。
  在JDK6中,為了提高效能,引入了偏向鎖和輕量級鎖。

2)為什麼要有偏向鎖?

大多數情況下,鎖不僅不存在多執行緒競爭,而且總是由同一執行緒多次獲得,為了減少執行緒獲得鎖的代價,所以引入了偏向鎖

3)為什麼要有重量級鎖?

自旋鎖消耗CPU資源,重量級鎖有等待佇列,不會消耗CPU資源

4)偏向鎖是否一定比自旋鎖效率高?

不一定,在多執行緒競爭情況下,偏向鎖會涉及鎖復原,這時候應該直接使用自旋鎖

5)鎖重入

重入次數必須記錄,才能知道要解鎖幾次

  • 輕量級鎖,記錄線上程棧,每插入一次,LockRecord+1
  • 重量級鎖,記錄在ObjectMonitor欄位上

6)Hopspot物件頭就是MarkWord?

不是的,Hopspot物件頭主要包括兩部分資料:MarkWord(標記欄位) 和 classPointer(類指標)

7)鎖可以降級?

不行的,是一個不可逆的過程,主要是為了提高獲得鎖和釋放鎖的效率

8)鎖對比,適用場景?

偏向鎖:適用於一個執行緒,不會有鎖消耗,鎖復原
輕量級鎖:適用於多個執行緒競爭,但同步程式碼塊執行快的情況下,因為自旋會消耗CPU
重量級鎖:適用於多個執行緒競爭,但同步程式碼塊執行慢的情況下,不消耗CPU,可以提高吞吐量