java中的鎖膨脹

無鎖->偏向鎖(無競爭的鎖)->輕量級鎖(能夠自旋的鎖)->重量級鎖
1、偏向鎖
在瞭解偏向鎖以前咱們先來了解一下對象頭編程

一、對象頭
在這裏引用《Java併發編程的藝術》中對對象頭的解釋數組

synchronized用的鎖是存在Java對象頭裏的。若是對象是數組類型,則虛擬機用3個字寬(Word)存儲對象頭,若是對象是非數組類型,則用2字寬存儲對象頭。在32位虛擬機中,1字寬等於4字節,即32bit,如表2-2所示:多線程

Java對象頭裏的Mark Word裏默認存儲對象的HashCode、分代年齡和鎖標記位。32位JVM的Mark Word的默認存儲結構如表2-3所示:併發

在運行期間,Mark Word裏存儲的數據會隨着鎖標誌位的變化而變化。Mark Word可能變化爲存儲如下4種數據,如表2-4所示:測試

在64位虛擬機下,Mark Word是64bit大小的,其存儲結構如表2-5所示:操作系統

二、偏向鎖
大多數狀況下,鎖不只不存在多線程競爭,並且老是由同一線程屢次得到,爲了讓線程得到鎖的代價更低而引入偏向鎖。
當一個線程訪問同步代碼塊並獲取鎖時,會在對象頭和棧幀中的鎖記錄裏存儲鎖偏向的線程ID,
之後該線程再進入和退出同步塊時不須要進行CAS操做來加鎖和解鎖,只須要簡單地測試一下對象頭的Mark Word裏是否存儲着指向當前線程的偏向鎖。
若是測試成功,表示線程已經得到了鎖。
若是測試失敗,則須要再測試一下Mark Word中偏向鎖的標識是否設置爲1(表示指向當前進程):
若是沒有,則使用CAS競爭鎖;若是設置了,則嘗試使用CAS將對象頭的偏向鎖指向當前進程。
——《Java併發編程的藝術》線程

偏向鎖的操做根本沒有去找操做系統, 每一個對象都有對象頭,下圖示爲account對象的「對象頭」指針

JVM使用CAS操做把線程ID記錄到了這個Mark Word當中,修改了標識位,當前線程就擁有這把鎖了
能夠看出:JVM不用和操做系統協商設置Mutex,它只記錄下線程ID,就表示當前線程擁有這把鎖了,不用操做系統介入。對象

這時線程得到了鎖,能夠執行synchronized修飾的代碼塊。進程

當線程再次執行到這個synchronized的時候,JVM經過鎖對象account的Mark Word判斷:「當前線程ID還在,還持有着這個對象的鎖,就能夠繼續進入臨界區執行

這就是偏向鎖,在沒有別的線程競爭的時候,一直偏向當前線程,當前線程能夠一直執行

2、自旋鎖(輕量級鎖)
輕量級鎖是由偏向所升級來的,偏向鎖運行在一個線程進入同步塊的狀況下,當第二個線程加入鎖爭用的時候,偏向鎖就會升級爲輕量級鎖

輕量級鎖的加鎖過程:

在代碼進入同步塊的時候,若是同步對象鎖狀態爲無鎖狀態(鎖標誌位爲「01」狀態,是否爲偏向鎖爲「0」),虛擬機首先將在當前線程的棧幀中創建一個名爲鎖記錄(Lock Record)的空間,用於存儲鎖對象目前的Mark Word的拷貝,官方稱之爲 Displaced Mark Word。

拷貝對象頭中的Mark Word複製到鎖記錄中;

拷貝成功後,虛擬機將使用CAS操做嘗試將對象的Mark Word更新爲指向Lock Record的指針,並將Lock record裏的owner指針指向object mark word。若是更新成功,則執行步驟4,不然執行步驟5。

若是這個更新動做成功了,那麼這個線程就擁有了該對象的鎖,而且對象Mark Word的鎖標誌位設置爲「00」,即表示此對象處於輕量級鎖定狀態。

若是這個更新操做失敗了,虛擬機首先會檢查對象的Mark Word是否指向當前線程的棧幀,若是是就說明當前線程已經擁有了這個對象的鎖,那就能夠直接進入同步塊繼續執行。不然說明多個線程競爭鎖,輕量級鎖就要膨脹爲重量級鎖,鎖標誌的狀態值變爲「10」,Mark Word中存儲的就是指向重量級鎖(互斥量)的指針,後面等待鎖的線程也要進入阻塞狀態。 而當前線程便嘗試使用自旋來獲取鎖,自旋就是爲了避免讓線程阻塞,而採用循環去獲取鎖的過程。

3、重量級鎖
輕量級鎖膨脹以後,就升級爲重量級鎖了。
重量級鎖是依賴對象內部的monitor鎖來實現的,而monitor又依賴操做系統的MutexLock(互斥鎖)來實現的,因此重量級鎖也被成爲互斥鎖。

synchronized就是一個典型的重量級鎖 synchronized關鍵字

相關文章
相關標籤/搜索