1. 程式人生 > >Java併發——Synchronized關鍵字和鎖升級,詳細分析偏向鎖和輕量級鎖的升級

Java併發——Synchronized關鍵字和鎖升級,詳細分析偏向鎖和輕量級鎖的升級

一、Synchronized實現原理

1、Synchronized鎖的3中形式:

Synchronized修飾普通同步方法:鎖物件當前例項物件;

Synchronized修飾靜態同步方法:鎖物件是當前的類Class物件;

Synchronized修飾同步程式碼塊:鎖物件是Synchronized後面括號裡配置的物件;

2、Synchronized在JVM裡的實現

那麼Synchronized在JVM裡是怎麼實現的呢?

在JVM規範裡可以看到Synchronized鎖時基於進入和退出monitor物件來實現方法同步和程式碼塊同步,這兩種同步的具體實現細節不太一樣,但是都可以這麼理解:不管是方法同步還是程式碼塊同步都是通過monitor物件的monitorentry和monitorexit這兩個指令實現的,在需要同步的程式碼塊開始的位置插入monitorentry指令,在同步結束的位置或者異常出現的位置插入monitorexit指令;JVM要保證monitorentry和monitorexit都是成對出現的,任何物件都有一個monitor閾值對應,當這個物件的monitor被持有以後,它將處於鎖定狀態。

3、Java物件頭

那麼Synchronized鎖物件是存在哪裡的呢?答案是存在鎖物件的物件頭中,稱為MarkWord。如果物件是陣列物件,那麼MarkWord佔用3個字寬(Word),如果物件是非陣列物件,那麼MarkWord佔用2個字寬。(1word = 2 Byte = 16 bit)

那麼MarkWord在物件頭中到底長什麼樣,也就是它到底儲存了什麼呢?

在32位的虛擬機器中:

在64位的虛擬機器中:

二、鎖升級

1、鎖的4中狀態:無鎖狀態、偏向鎖狀態、輕量級鎖狀態、重量級鎖狀態(級別從低到高)

2、偏向鎖:

為什麼要引入偏向鎖?

因為經過HotSpot的作者大量的研究發現,大多數時候是不存在鎖競爭的,常常是一個執行緒多次獲得同一個鎖,因此如果每次都要競爭鎖會增大很多沒有必要付出的代價,為了降低獲取鎖的代價,才引入的偏向鎖。

偏向鎖的升級

當執行緒1訪問程式碼塊並獲取鎖物件時,會在java物件頭和棧幀中記錄偏向的鎖的threadID,因為偏向鎖不會主動釋放鎖,因此以後執行緒1再次獲取鎖的時候,需要比較當前執行緒的threadID和Java物件頭中的threadID是否一致,如果一致(還是執行緒1獲取鎖物件),則無需使用CAS來加鎖、解鎖;如果不一致(其他執行緒,如執行緒2要競爭鎖物件,而偏向鎖不會主動釋放因此還是儲存的執行緒1的threadID),那麼需要檢視Java物件頭中記錄的執行緒1是否存活,如果沒有存活,那麼鎖物件被重置為無鎖狀態,其它執行緒(執行緒2)可以競爭將其設定為偏向鎖;如果存活,那麼立刻查詢該執行緒(執行緒1)的棧幀資訊,如果還是需要繼續持有這個鎖物件,那麼暫停當前執行緒1,撤銷偏向鎖,升級為輕量級鎖,如果執行緒1 不再使用該鎖物件,那麼將鎖物件狀態設為無鎖狀態,重新偏向新的執行緒。

偏向鎖的取消:

偏向鎖是預設開啟的,而且開始時間一般是比應用程式啟動慢幾秒,如果不想有這個延遲,那麼可以使用-XX:BiasedLockingStartUpDelay=0;

如果不想要偏向鎖,那麼可以通過-XX:-UseBiasedLocking = false來設定;

3、輕量級鎖

為什麼要引入輕量級鎖?

輕量級鎖考慮的是競爭鎖物件的執行緒不多,而且執行緒持有鎖的時間也不長的情景。因為阻塞執行緒需要CPU從使用者態轉到核心態,代價較大,如果剛剛阻塞不久這個鎖就被釋放了,那這個代價就有點得不償失了,因此這個時候就乾脆不阻塞這個執行緒,讓它自旋這等待鎖釋放。

輕量級鎖什麼時候升級為重量級鎖?

執行緒1獲取輕量級鎖時會先把鎖物件的物件頭MarkWord複製一份到執行緒1的棧幀中建立的用於儲存鎖記錄的空間(稱為DisplacedMarkWord),然後使用CAS把物件頭中的內容替換為執行緒1的鎖記錄地址;

如果線上程1複製物件頭的同時(線上程1CAS之前),執行緒2也準備獲取鎖,複製了物件頭到執行緒2的鎖記錄空間中,但是線上程2CAS的時候,發現執行緒1已經把物件頭換了,執行緒2的CAS失敗,那麼執行緒2就嘗試使用自旋鎖來等待執行緒1釋放鎖。

但是如果自旋的時間太長也不行,因為自旋是要消耗CPU的,因此自旋的次數是有限制的,比如10次或者100次,如果自旋次數到了執行緒1還沒有釋放鎖,或者執行緒1還在執行,執行緒2還在自旋等待,這時又有一個執行緒3過來競爭這個鎖物件,那麼這個時候輕量級鎖就會膨脹為重量級鎖。重量級鎖把除了擁有鎖的執行緒都阻塞,防止CPU空轉。

*注意:為了避免無用的自旋,輕量級鎖一旦膨脹為重量級鎖就不會再降級為輕量級鎖了;偏向鎖升級為輕量級鎖也不能再降級為偏向鎖。一句話就是鎖可以升級不可以降級,但是偏向鎖狀態可以被重置為無鎖狀態。

4、這幾種鎖的優缺點(偏向鎖、輕量級鎖、重量級鎖)

轉載:https://blog.csdn.net/tongdanping/article/details/79647337?utm_source=copy