synchronized底層實現原理及鎖優化

2022-09-20 08:33:11 字數 4064 閱讀 3295

一、概述

1、synchronized作用

原子性:synchronized保證語句塊內操作是原子的

可見性:synchronized保證可見性(通過「在執行unlock之前,必須先把此變數同步回主記憶體」實現)

有序性:synchronized保證有序性(通過「乙個變數在同一時刻只允許一條執行緒對其進行lock操作」)

2、synchronized的使用

修飾例項方法,對當前例項物件加鎖

修飾靜態方法,多當前類的class物件加鎖

修飾**塊,對synchronized括號內的物件加鎖

二、實現原理

1、jvm基於進入和退出monitor物件來實現方法同步和**塊同步。

方法級的同步是隱式,即無需通過位元組碼指令來控制的,它實現在方法呼叫和返回操作之中。jvm可以從方法常量池中的方法表結構(method_info structure) 中的 acc_synchronized 訪問標誌區分乙個方法是否同步方法。當方法呼叫時,呼叫指令將會 檢查方法的 acc_synchronized 訪問標誌是否被設定,如果設定了,執行執行緒將先持有monitor(虛擬機器規範中用的是管程一詞), 然後再執行方法,最後再方法完成(無論是正常完成還是非正常完成)時釋放monitor。

**塊的同步是利用monitorenter和monitorexit這兩個位元組碼指令。它們分別位於同步**塊的開始和結束位置。當jvm執行到monitorenter指令時,當前執行緒試圖獲取monitor物件的所有權,如果未加鎖或者已經被當前執行緒所持有,就把鎖的計數器+1;當執行monitorexit指令時,鎖計數器-1;當鎖計數器為0時,該鎖就被釋放了。如果獲取monitor物件失敗,該執行緒則會進入阻塞狀態,直到其他執行緒釋放鎖。

這裡要注意:

synchronized是可重入的,所以不會自己把,自己鎖死

synchronized鎖一旦被乙個執行緒持有,其他試圖獲取該鎖的執行緒將被阻塞。

關於acc_synchronized 、monitorenter、monitorexit指令,可以看一下下面的反編譯**:

public class synchronizeddemo

public void g()

}public static void main(string args) }1

2345

6789

1011

1213

使用j**ap -verbose synchronizeddemo反編譯後得到

我們看到對於同步方法,反編譯後得到acc_synchronized 標誌,對於同步**塊反編譯後得到monitorenter和monitorexit指令。

三、理解j**a物件頭

在jvm中,物件在記憶體中的布局分為三塊區域:物件頭、例項資料和對齊填充。

例項變數:存放類的屬性資料資訊,包括父類的屬性資訊,如果是陣列的例項部分還包括陣列的長度,這部分記憶體按4位元組對齊。

hotspot虛擬機器的物件頭分為兩部分資訊,第一部分用於儲存物件自身執行時資料,如雜湊碼、gc分代年齡等,這部分資料的長度在32位和64位的虛擬機器中分別為32位和64位。官方稱為mark word。另一部分用於儲存指向物件型別資料的指標,如果是陣列物件的話,還會有乙個額外的部分儲存陣列長度。

虛擬機器位數    物件頭結構    描述

32位/64位    mark word    儲存物件的雜湊碼、gc分代年齡、鎖資訊等

32位/64位    class metadata address    指向物件型別資料的指標

32位/64位    陣列長度    如果是陣列物件的話,有這一部分,否則沒有

由於物件頭的資訊是與物件自身定義的資料沒有關係的額外儲存成本,因此考慮到jvm的空間效率,mark word 被設計成為乙個非固定的資料結構,以便儲存更多有效的資料,它會根據物件本身的狀態復用自己的儲存空間。

四、jvm對synchronized的鎖優化

synchronized是通過物件內部的乙個叫做監視器鎖(monitor)來實現的,監視器鎖本質又是依賴於底層的作業系統的mutex lock(互斥鎖)來實現的。而作業系統實現執行緒之間的切換需要從使用者態轉換到核心態,這個成本非常高,狀態之間的轉換需要相對比較長的時間,這就是為什麼synchronized效率低的原因。因此,這種依賴於作業系統mutex lock所實現的鎖我們稱之為「重量級鎖」。

j**a se 1.6為了減少獲得鎖和釋放鎖帶來的效能消耗,引入了「偏向鎖」和「輕量級鎖」:鎖一共有4種狀態,級別從低到高依次是:無鎖狀態、偏向鎖狀態、輕量級鎖狀態和重量級鎖狀態。鎖可以公升級但不能降級。

1、偏向鎖

偏向鎖是jdk1.6中引用的優化,它的目的是消除資料在無競爭情況下的同步原語,進一步提高程式的效能。

偏向鎖的獲取:

判斷是否為可偏向狀態

如果為可偏向狀態,則判斷執行緒id是否是當前執行緒,如果是進入同步塊;

如果執行緒id並未指向當前執行緒,利用cas操作競爭鎖,如果競爭成功,將mark word中線程id更新為當前執行緒id,進入同步塊

如果競爭失敗,等待全域性安全點,準備撤銷偏向鎖,根據執行緒是否處於活動狀態,決定是轉換為無鎖狀態還是公升級為輕量級鎖。

當鎖物件第一次被執行緒獲取的時候,虛擬機會把物件頭中的標誌位設定為「01」,即偏向模式。同時使用cas操作把獲取到這個鎖的執行緒id記錄在物件的mark word中,如果cas操作成功。持有偏向鎖的執行緒以後每次進入這個鎖相關的同步塊時,虛擬機器都可以不再進行任何同步操作。

偏向鎖的釋放:

偏向鎖使用了遇到競爭才釋放鎖的機制。偏向鎖的撤銷需要等待全域性安全點,然後它會首先暫停擁有偏向鎖的執行緒,然後判斷執行緒是否還活著,如果執行緒還活著,則公升級為輕量級鎖,否則,將鎖設定為無鎖狀態。

2、輕量級鎖

輕量級鎖也是在jdk1.6中引入的新型鎖機制。它不是用來替換重量級鎖的,它的本意是在沒有多執行緒競爭的情況下,減少傳統的重量級鎖使用作業系統互斥量產生的效能消耗。

加鎖過程:

在**進入同步塊的時候,如果此物件沒有被鎖定(鎖標誌位為「01」狀態),虛擬機器首先在當前執行緒的棧幀中建立乙個名為鎖記錄(lock record)的空間,用於儲存物件目前mark word的拷貝(官方把這份拷貝加了乙個displaced字首,即displaced mark word)。然後虛擬機器使用cas操作嘗試將物件的mark word更新為指向鎖記錄(lock record)的指標。如果更新成功,那麼這個執行緒就擁有了該物件的鎖,並且物件的mark word標誌位轉變為「00」,即表示此物件處於輕量級鎖定狀態;如果更新失敗,虛擬機器首先會檢查物件的mark word是否指向當前執行緒的棧幀,如果說明當前執行緒已經擁有了這個物件的鎖,那就可以直接進入同步塊中執行,否則說明這個鎖物件已經被其他執行緒占有了。如果有兩條以上的執行緒競爭同乙個鎖,那輕量級鎖不再有效,要膨脹為重量級鎖,鎖標誌變為「10」,mark word中儲存的就是指向重量級鎖的指標,而後面等待的執行緒也要進入阻塞狀態。

解鎖過程:

如果物件的mark word仍然指向執行緒的鎖記錄,那就用cas操作將物件當前的mark word與執行緒棧幀中的displaced mark word交換回來,如果替換成功,整個同步過程就完成了。如果替換失敗,說明有其他執行緒嘗試過獲取該鎖,那就要在釋放鎖的同時,喚醒被掛起的執行緒。

如果沒有競爭,輕量級鎖使用cas操作避免了使用互斥量的開銷,但如果存在競爭,除了互斥量的開銷外,還額外發生了cas操作,因此在有競爭的情況下,輕量級鎖比傳統重量級鎖開銷更大。

3、重量級鎖

synchronized的重量級鎖是通過物件內部的乙個叫做監視器鎖(monitor)來實現的,監視器鎖本質又是依賴於底層的作業系統的mutex lock(互斥鎖)來實現的。而作業系統實現執行緒之間的切換需要從使用者態轉換到核心態,這個成本非常高,狀態之間的轉換需要相對比較長的時間,這就是為什麼synchronized效率低的原因。

4、自旋鎖

互斥同步對效能影響最大的是阻塞的實現,掛起執行緒和恢復執行緒的操作都需要轉入到核心態中完成,這些操作給系統的併發效能帶來很大的壓力。

於是在阻塞之前,我們讓執行緒執行乙個忙迴圈(自旋),看看持有鎖的執行緒是否釋放鎖,如果很快釋放鎖,則沒有必要進行阻塞。

5、鎖消除

鎖消除是指虛擬機器即時編譯器(jit)在執行時,對一些**上要求同步,但是檢測到不可能發生資料競爭的鎖進行消除。

6、鎖粗化

如果虛擬機器檢測到有這樣一串零碎的操作都對同乙個物件加鎖,將會把加鎖同步的範圍擴充套件(粗化)到整個操作序列的外部。

synchronized底層實現原理(保證看懂)

首先那些說看過synchronized原始碼的基本都是大聰明,synchronized根本點不進去,想弄懂它的實現原理,我們只能通過看編譯好的位元組碼檔案 整個測試類 public class synchronizedtest 位元組碼檔案 怎麼看?idea view show bytecode 再...

synchronized底層原理學習

這個是以sychronized修飾物件進行分析,假設使用了a物件進行加鎖,那麼a物件就是鎖,a物件包含有物件頭 資料和填充組成,其中物件頭包含mark word和型別指標,mark word會指向乙個監視器物件 指向監視器的引用 監視器物件包含有執行緒持有者和計數器等資訊。mark word包括有物...

ConcurrentHashMap底層實現

concurrenthashmap融合了hashtable和hashmap二者的優勢 hashtable是做了同步的,hashmap沒有同步,所以hashmap在單執行緒情況下效率高,hashtable在多執行緒情況下,同步操作能保證程式執行的正確性 但是hashtable每次同步執行都要鎖住整個結...