每天資訊Synchronized的底層實現和鎖升級

菜單

Synchronized的底層實現和鎖升級

Synchronized

JVM層面的鎖,鎖機制存在升級。JDK1。6之後,Synchronize最初是偏向鎖,然後升級為輕量級鎖,最後升到重量級鎖。

鎖升級是什麼樣的,什麼情況下可以進行鎖升級?

Synchronized的底層實現和鎖升級

Java是面向物件程式設計,物件的結構大體可以分為三個部分:物件頭(object Header),物件的例項資料(指標),對齊(8位元組對齊);

物件頭的預設儲存的元素是:物件的hashcode,GC分代年齡,鎖標記位。

Synchronized的底層實現和鎖升級

鎖一共有四種狀態,級別從低到高依次是:無鎖狀態、偏向鎖狀態、輕量級鎖狀態和重量級鎖狀態,這幾個狀態隨著競爭情況逐漸升級。為了提高獲得鎖和釋放鎖的效率,鎖可以升級但不能降級,意味著偏向鎖升級為輕量級鎖後不能降級為偏向鎖。

偏向鎖

偏向鎖的鎖標記位是01,JDK設定引數為-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0。偏向鎖是預設開啟的,開啟的時候存在延長時間。當JVM啟動載入資源的時候,初始化的物件加上偏向鎖會耗費大量的資源,所以存在偏向鎖可以減少偏向鎖撤銷的成本。

當一個執行緒訪問同步塊或者Synchronize修飾的方法獲取鎖的時候,會在物件頭以及執行緒自身的棧幀的鎖記錄中儲存偏向鎖的執行緒ID,以後當前執行緒進入和退出同步程式碼塊的時候就無需在進行加鎖,只需要驗證物件頭中的執行緒ID是否是當前執行緒,如果測試成功,表示執行緒已經獲得了鎖。如果測試失敗,則需要判斷偏向鎖的標識。如果標識被設定為0(表示當前是無鎖狀態),則使用CAS競爭鎖;如果標識設定成1(表示當前是偏向鎖狀態),則嘗試使用CAS將物件頭的偏向鎖指向當前執行緒,觸發偏向鎖的撤銷。

偏向鎖只有在競爭出現才會釋放鎖。當其他執行緒嘗試競爭偏向鎖時,程式到達全域性安全點後(沒有正在執行的程式碼),它會檢視Java物件頭中記錄的執行緒是否存活,如果沒有存活,那麼鎖物件被重置為無鎖狀態,其它執行緒可以競爭將其設定為偏向鎖;如果存活,那麼立刻查詢該執行緒的棧幀資訊,如果還是需要繼續持有這個鎖物件,那麼暫停當前執行緒,撤銷偏向鎖,升級為輕量級鎖,如果執行緒1不再使用該鎖物件,那麼將鎖物件狀態設為無鎖狀態,重新偏向新的執行緒。

Synchronized的底層實現和鎖升級

輕量級鎖

輕量級鎖的鎖標記位是00,輕量級鎖可以理解為自旋鎖,當多個執行緒訪問同一個同步塊的時候,透過CAS操作,只有一個執行緒獲取到鎖,其他執行緒不斷處於自旋狀態。所謂自旋,就是指當有另外一個執行緒來競爭鎖時,這個執行緒會在原地迴圈等待,而不是把該執行緒給阻塞,直到那個獲得鎖的執行緒釋放鎖之後,這個執行緒就可以馬上獲得鎖的。執行緒不斷的自旋操作會佔用CPU的資源。

獲取到鎖的執行緒會在棧幀中建立用於儲存鎖記錄的空間,並把同步塊的物件頭複製到鎖記錄中。同步塊的物件頭替換成為指向鎖記錄的指標。

執行緒自旋獲取鎖的次數達到一定的數量,鎖會自動上升為重量級鎖。

Synchronized的底層實現和鎖升級

重量級鎖

重量級鎖會阻塞、喚醒執行緒,為什麼講重量級鎖耗費的資源比較大呢?阻塞執行緒是不會消耗CPU的,但是阻塞和喚醒執行緒都是作業系統進行的,需要從使用者態轉換為核心態,這個狀態的轉換是需要耗費很多的時間片。

Synchronized的底層實現和鎖升級

Synchronize底層是如何實現的?

建議自己寫一個Demo,然後在java檔案的目錄下,使用命令 javac SynchronizedTest。java 編譯,在當前目錄會生成SynchronizedTest。class檔案, 接著輸入 javap -c SynchronizedTest 命令,將位元組碼檔案進行反彙編。

public class SynchronizedTest {

public synchronized void hello() {

System。out。println(“hello world synchronized”);

}

}

Synchronized的底層實現和鎖升級

可以從圖中看到存在兩個位元組碼指令monitorenter和monitorexit。

monitor物件

獲取物件鎖的過程,就是獲取monitor物件的所有權的過程。Monitor物件的結構如下:

Synchronized的底層實現和鎖升級

當多個執行緒同時訪問一段同步程式碼時,這些執行緒會被放到一個EntryList集合中,處於阻塞狀態的執行緒都會被放到該列表當中。接下來,當執行緒獲取到物件的Monitor時,Monitor是依賴於底層作業系統的mutexlock(互斥鎖)來實現互斥的,執行緒獲取mutex成功,則會持有該mutex,這時其他執行緒就無法再獲取到該mutex。

如果執行緒呼叫了wait方法,那麼該執行緒就會釋放掉所持有的mutex,並且該執行緒會進入到WaitSet集合(等待集合)中,等待下一次被其他執行緒呼叫notify/notifyAll喚醒。如果當前執行緒順利執行完畢方法,那麼它也會釋放掉所持有的mutex。OnDeck表示任意時刻,最多隻有一個執行緒正在競爭鎖資源,當前已經獲取到所資源的執行緒被稱為Owner。

SynchroniSze是非公平鎖,不需要開發人員手工去釋放鎖。

SynchroniSze鎖的使用。

Synchronized的底層實現和鎖升級

本來還想寫一下Lock鎖以及讀寫鎖、volatile來著,看了下時間太晚了,明天寫把。

Synchronized的底層實現和鎖升級

我是凱騰凱,網際網路浪潮下一枚苟且偷生的程式猿,各位老鐵,點個關注唄