1. 程式人生 > >java 執行緒——偏向鎖&輕量級鎖&重量級鎖

java 執行緒——偏向鎖&輕量級鎖&重量級鎖

偏向鎖

輕量級鎖

重量級鎖

執行緒阻塞的代價

java的執行緒是對映到作業系統原生執行緒之上的,如果要阻塞或喚醒一個執行緒就需要作業系統介入,需要在戶態與核心態之間切換,這種切換會消耗大量的系統資源。

我們所熟知的Synchronized 在競爭鎖失敗的情況下就會進入阻塞狀態,這種執行緒策略效率非常的低。

Mark word 

我們先來看看虛擬機器中物件的結構,我們主要來看一下物件頭。物件頭資訊是是與物件自身資訊無關的額外儲存成本,考慮到虛擬機器的空間效率,虛擬機器被設計成了一個非固定長度的資料結構以便在極小的空間內儘量儲存更多的資訊,它會根據物件的狀態複用自己的儲存空間。

例如在32 位的HotSpot 虛擬機器中物件未鎖定的狀態下,Mark Word 的32 bit 空間中的25bit被用於儲存物件的雜湊嗎,4 bit 用於儲存物件分代年齡,2bit用於儲存物件的鎖標誌位,1bit固定為0,在其他狀態下物件的儲存內容見下圖

偏向鎖

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

假設當前虛擬機器啟用了偏向鎖,當鎖物件第一次被執行緒獲取的時候,虛擬機器會把物件頭中的鎖標誌為設定為“01”,同時用CAS 操作把獲取這把鎖的執行緒ID 記錄在物件頭之中,如果CAS 成功,持有偏向鎖的執行緒以後每次進入這個鎖的相關程式碼塊時,虛擬機器都不用再進行任何同步操作。

當另一個執行緒嘗試獲取這個鎖的時候,偏向模式結束。根據鎖物件目前是否處於被鎖定的狀態,撤銷偏向後恢復到未鎖定或者輕量級鎖狀態。

上面這段解釋來自於《深入理解 java 虛擬機器》

從這段話我們可以看出偏向鎖是在沒有執行緒競爭的情況下使用,當有現成來競爭時,偏向鎖便膨脹為輕量級鎖。

輕量級鎖

輕量級鎖是從偏向鎖膨脹來的。

輕量級鎖的加鎖過程

在程式碼進入同步塊的時候,如果同步物件鎖狀態為無鎖狀態(鎖標誌位為“01”狀態,是否為偏向鎖為“0”),虛擬機器首先將在當前執行緒的棧幀中建立一個名為鎖記錄(Lock Record)的空間,用於儲存鎖物件目前的Mark Word的拷貝,官方稱之為 Displaced Mark Word。這時候執行緒堆疊與物件頭的狀態如圖:

這裡寫圖片描述

拷貝物件頭中的Mark Word複製到鎖記錄中; 拷貝成功後,虛擬機器將使用CAS操作嘗試將物件的Mark Word更新為指向Lock Record的指標,並將Lock record裡的owner指標指向object mark word。

如果這個更新動作成功了,那麼這個執行緒就擁有了該物件的鎖,並且物件Mark Word的鎖標誌位設定為“00”,即表示此物件處於輕量級鎖定狀態,這時候執行緒堆疊與物件頭的狀態如圖所示。

  

如果這個更新操作失敗了,虛擬機器首先會檢查物件的Mark Word是否指向當前執行緒的棧幀,如果是就說明當前執行緒已經擁有了這個物件的鎖,那就可以直接進入同步塊繼續執行。否則說明多個執行緒競爭鎖,輕量級鎖就要膨脹為重量級鎖,鎖標誌的狀態值變為“10”,Mark Word中儲存的就是指向重量級鎖(互斥量)的指標,後面等待鎖的執行緒也要進入阻塞狀態。 而當前執行緒便嘗試使用自旋來獲取鎖,自旋就是為了不讓執行緒阻塞,而採用迴圈去獲取鎖的過程。

釋放執行緒鎖的過程

它的解鎖過程也是通過CAS 操作來進行的 。

如果物件Mark Word 仍指著執行緒的鎖記錄,那就用CAS 把物件當前的Mark Word 和執行緒中複製的Displaced Mark Word 替換回來。

若替換成功,整個同步過程就完成了。

若替換失敗,說明有其它執行緒嘗試獲取過該鎖,那就在釋放鎖的同時喚醒被掛起的執行緒。

偏向鎖、輕量級鎖的狀態轉化以及物件Mark Word 的關係如圖

重量級鎖

重量級鎖的加鎖、解鎖過程和輕量級鎖差不多,區別是:競爭失敗後,執行緒阻塞,釋放鎖後,喚醒阻塞的執行緒,不使用自旋鎖,不會那麼消耗CPU,所以重量級鎖適合用在同步塊執行時間長的情況下。