Java中鎖的種類大致分為偏向鎖,自旋鎖,輕量級鎖,重量級鎖。
鎖的使用方式為:先提供偏向鎖,如果不滿足的時候,升級為輕量級鎖,再不滿足,升級為重量級鎖。自旋鎖是一個過度的鎖狀態(tài),不是一種實際的鎖類型。
-
重量級鎖
image.png
image.png
對象頭:儲存對象第hashCode、鎖信息或分代年齡或GC標(biāo)志,類型指針指向?qū)ο蟮念愒獢?shù)據(jù),JVM通過這個指針確定改對象是哪個類的實例等信息。
實例變量:存放類的屬性數(shù)據(jù)信息,包括父類的屬性信息
填充數(shù)據(jù):由于虛擬機(jī)要求對象其實地址必須是8字節(jié)的整數(shù)倍。填充數(shù)據(jù)不是必須存在的,僅僅是為類字節(jié)對齊
當(dāng)在對象上加鎖時,數(shù)據(jù)是記錄在對象頭中。當(dāng)執(zhí)行synchronized同步方法或同步代碼塊時,會在對象頭中記錄鎖標(biāo)記,標(biāo)記指向的是monitor對象(也稱為管程或監(jiān)視器鎖)的其實地址。每個對象都存在著一個monitor與之關(guān)聯(lián),對象與其monitor之間的關(guān)系有存在多種實現(xiàn)方式,如monitor可以與對象一起創(chuàng)建銷毀或當(dāng)線程試圖獲取對象鎖時自動生成,當(dāng)一個monitor被某個線程持有后,它便處于鎖定狀態(tài)。
在Java虛擬機(jī)(HotSpot)中,monitor是由ObjectMonitor實現(xiàn)的。
ObjectMonitor中有兩個隊列,_WaitSet和_EntryList,以及_Owner標(biāo)記。其中_WaitSet是用于管理等待隊列(wait)線程的,_EntryList是用于管理鎖池阻塞線程的,_Owner標(biāo)記用來記錄當(dāng)前執(zhí)行線程。

當(dāng)多線程并發(fā)訪問同一個同步代碼時,首先會進(jìn)入_EntryList,當(dāng)線程獲取鎖標(biāo)記后,monitor中當(dāng)_Owner記錄此線程,并在 monitor中當(dāng)計數(shù)器執(zhí)行遞增計算(+1),代表鎖定,其他線程在 _EntryList中繼續(xù)阻塞。若執(zhí)行線程調(diào)用wait方法,擇monitor中的計數(shù)器執(zhí)行賦值為0計算,并講_Owner標(biāo)記復(fù)制為null,代表放棄鎖,執(zhí)行進(jìn)程進(jìn)入_WaitSet中阻塞。若執(zhí)行進(jìn)程調(diào)用notify/notifyAll方法,_WaitSet中的線程被喚醒,進(jìn)入_EntryList中阻塞,等待獲取鎖標(biāo)記。若執(zhí)行線程的同步代碼執(zhí)行結(jié)束,同樣會是否鎖標(biāo)記,monitor中_Owner標(biāo)記賦值為null,且計數(shù)器自減計算 (-1)。
- 偏向鎖
是一種編譯解釋鎖。如果代碼中不可能出現(xiàn)多線程兵法爭搶同一個鎖的時候,JVM編譯代碼,解釋執(zhí)行的時候,會自動放棄同步信息。消除synchronized的同步代碼結(jié)果。使用鎖標(biāo)記的形式記錄鎖狀態(tài)。再Monitor中有變量ACC_SYNCHRONIZED。當(dāng)變量值使用的時候,代表偏向鎖鎖定??梢员苊怄i的爭搶和鎖池狀態(tài)的維護(hù),提高效率。 - 輕量級鎖
過度鎖。當(dāng)偏向鎖不滿足,也就是有多線程并發(fā)訪問,鎖定同一個對象的時候,先提升為輕量級鎖。也是使用標(biāo)記ACC_SYNCHRONIZED標(biāo)記記錄的。ACC_UNSYNCHRONIZED標(biāo)記記錄未獲取到鎖信息的線程。就是只有兩個線程爭搶鎖標(biāo)記的時候,優(yōu)先使用輕量級鎖。
兩個線程也可能出現(xiàn)重量級鎖。 - 自旋鎖
是一個過渡鎖,是偏向鎖和輕量級鎖的過渡。
當(dāng)獲取鎖的過程中,未獲取到。為了提高效率,JVM自動執(zhí)行若干次空循環(huán),在次申請鎖,而不是進(jìn)入阻塞狀態(tài)的情況。稱為自旋鎖。

