synchronized 修飾一個代碼塊時
,編譯後會在同步塊的先後分別造成monitorenter和monitorexit這兩個字節碼指令
,而當虛擬機執行monitorenter指令時,會去嘗試獲取對象的鎖,若是這個對象沒有被鎖定或者當前線程已經持有了這個對象的鎖(可重入性),就會把鎖的計數器加一,而在執行monitorexit指令時鎖的計數器就會減一;Mark Word
),通常爲32bit或64bit(由虛擬機的位數決定),當該對象處於未被鎖定的狀態時,MarkWord中有25bit用來存儲對象的hashCode(這裏猜想是爲了便於找到對象?與HashMap相似,但沒有深刻去了解),4bit用於存儲對象的分代年齡(GC相關),2bit用於存儲鎖標誌位,1bit默認爲0;每一個實例對象都會擁有一個等待隊列(即爲每一個實例準備的線程休息室),當線程處於鎖定狀態時,其餘線程須要等待獲取這個鎖時,會加入該對象的等待隊列(即圖中的EntryList),而後等待獲取鎖,即被Owner指針所指向,(若是此時被wait()方法掛起,則會進入WaitSet
),若是中間沒有被掛起過,則最後會調用monitorexit()方法釋放鎖,並將鎖的計數器減一;java
monitor對象 是由 ObjectMonitor()對象實現的
ObjectMonitor() {
_header = NULL;
_count = 0; //記錄個數
_waiters = 0,
_recursions = 0;
_object = NULL;
_owner = NULL;
_WaitSet = NULL; //處於wait狀態的線程,會被加入到_WaitSet
_WaitSetLock = 0 ;
_Responsible = NULL ;
_succ = NULL ;
_cxq = NULL ;
FreeNext = NULL ;
_EntryList = NULL ; //處於等待鎖block狀態的線程,會被加入到該列表
_SpinFreq = 0 ;
_SpinClock = 0 ;
OwnerIsThread = 0 ;
}
複製代碼
此時就能夠明白了,每一個對象對應的信息都是存儲在這的;數組
monitor對象
的指針時,說明有另外一個線程競爭了這個對象致使鎖膨脹(在變成重量級鎖前,競爭鎖的對象會適當自旋給定的次數,避免頻繁的線程掛起和喚醒,由於Java虛擬機的線程是映射到操做系統核心態的線程的,因此每次對於線程的操做,都會須要將系統轉至核心態,而這個開銷是比較大的,而這也是重量級鎖慢的主要緣由),因此後面等待鎖的線程也要進入阻塞狀態;finally代碼塊中
手動釋放鎖,而JVM會保證後者的鎖釋放;其次是由於如今二者的性能也比較接近;Stop The Wrold
,查看當前MarkWord中的線程是否還在運行,若是已經終止則將線程ID改成本身;若是以前的線程尚未中止運行,則須要解偏向鎖,升級成輕量級鎖;輕量級鎖適用於保護的代碼塊執行速度很快,且預計不會發生線程衝突的場景