1. 程式人生 > >java 併發之 synchronized 實現原理

java 併發之 synchronized 實現原理

在 java 開發中 synchronized 是使用的最多的工具。

表現形式

在 java 中每個物件都可以作為鎖:

  1. 對於普通同步方法,鎖是當前例項物件;
  2. 對於靜態同步方法,鎖是當前類的 Class 物件;
  3. 對於同步方法快,鎖是 Synchronized 括號裡配置的物件。

當一個執行緒試圖訪問同步程式碼塊時,它首先必須得到鎖。

實現原理

JVM 基於進入和退出 Monitor 物件來實現方法同步和程式碼塊同步。

程式碼示例:
程式碼示例

利用javap工具檢視生成的class檔案資訊來分析Synchronize的實現
反彙編

從上面可以看出,同步程式碼塊是使用 monitorenter 和 monitorexit 指令實現的。
而同步方法則是通過設定 ACC_SYNCHRONIZED 標誌來實現的,表示使用呼叫該方法的物件或該方法所屬的Class在JVM的內部物件做為鎖物件:
同步方法反編譯

java 物件頭

synchronized 用的鎖是存在Java物件頭裡的。java 物件頭包含:

  1. Mark Word : 儲存物件的HashCode、分代年齡和鎖資訊等。
  2. Class Metadata Address : 儲存到物件型別資料的指標
  3. Array length : 陣列的長度(只有當物件是陣列的時候才會包含這一資訊)

鎖優化

java 在發展過程中為了減少獲取鎖和釋放鎖帶來的效能消耗,引入了“偏向鎖”和“輕量級鎖”。鎖主要存在四中狀態,依次是:無鎖狀態、偏向鎖狀態、輕量級鎖狀態、重量級鎖狀態,他們會隨著對資源的競爭情況而逐漸升級,但是隻可以升級不可降級。

偏向鎖

引入偏向鎖主要目的是:為了在無多執行緒競爭的情況下儘量減少不必要的輕量級鎖執行路徑。處理流程如下:

獲取鎖

  1. 檢測Mark Word是否為可偏向狀態,即是否為偏向鎖1,鎖標識位為01;
  2. 若為可偏向狀態,則測試執行緒ID是否為當前執行緒ID,如果是,則執行步驟(5),否則執行步驟(3);
  3. 如果執行緒ID不為當前執行緒ID,則通過CAS操作競爭鎖,競爭成功,則將Mark Word的執行緒ID替換為當前執行緒ID,否則執行執行緒(4);
  4. 通過CAS競爭鎖失敗,證明當前存在多執行緒競爭情況,當到達全域性安全點,獲得偏向鎖的執行緒被掛起,偏向鎖升級為輕量級鎖,然後被阻塞在安全點的執行緒繼續往下執行同步程式碼塊;
  5. 執行同步程式碼塊

撤銷鎖

偏向鎖採用了一種只有競爭才會釋放鎖的機制,執行緒是不會主動去釋放偏向鎖,需要等待其他執行緒來競爭。偏向鎖的撤銷需要等待全域性安全點(safepoint)。其步驟如下:

  1. 暫停擁有偏向鎖的執行緒,判斷鎖物件是否還處於被鎖定狀態;
  2. 撤銷偏向鎖,恢復到無鎖狀態或者輕量級鎖的狀態;
    在這裡插入圖片描述

輕量級鎖

引入輕量級鎖的主要目的是在多沒有多執行緒競爭的前提下,減少傳統的重量級鎖使用作業系統互斥量產生的效能消耗。當關閉偏向鎖功能或者多個執行緒競爭偏向鎖導致偏向鎖升級為輕量級鎖,則會嘗試獲取輕量級鎖,其步驟如下:

獲取鎖

  1. 判斷當前物件是否處於無鎖狀態(hashcode、0、01),若是,則JVM首先將在當前執行緒的棧幀中建立一個名為鎖記錄(Lock Record)的空間,用於儲存鎖物件目前的Mark Word的拷貝(官方把這份拷貝加了一個Displaced字首,即Displaced Mark Word);否則執行步驟(3);
  2. JVM利用CAS操作嘗試將物件的Mark Word更新為指向Lock Record的指正,如果成功表示競爭到鎖,則將鎖標誌位變成00(表示此物件處於輕量級鎖狀態),執行同步操作;如果失敗則執行步驟(3);
  3. 判斷當前物件的Mark Word是否指向當前執行緒的棧幀,如果是則表示當前執行緒已經持有當前物件的鎖,則直接執行同步程式碼塊;否則只能說明該鎖物件已經被其他執行緒搶佔了,這時輕量級鎖需要膨脹為重量級鎖,鎖標誌位變成10,後面等待的執行緒將會進入阻塞狀態;

釋放鎖

輕量級鎖的釋放也是通過CAS操作來進行的,主要步驟如下:

  1. 取出在獲取輕量級鎖儲存在Displaced Mark Word中的資料;
  2. 用CAS操作將取出的資料替換當前物件的Mark Word中,如果成功,則說明釋放鎖成功,否則執行(3);
  3. 如果CAS操作替換失敗,說明有其他執行緒嘗試獲取該鎖,則需要在釋放鎖的同時需要喚醒被掛起的執行緒。

對於輕量級鎖,其效能提升的依據是“對於絕大部分的鎖,在整個生命週期內都是不會存在競爭的”,如果打破這個依據則除了互斥的開銷外,還有額外的CAS操作,因此在有多執行緒競爭的情況下,輕量級鎖比重量級鎖更慢;

在這裡插入圖片描述