【Java并發(fā)】月薪30K必須知道的Java鎖機(jī)制

并發(fā)環(huán)境下,多個(gè)線程會(huì)對同一個(gè)資源進(jìn)行爭搶,可能導(dǎo)致數(shù)據(jù)不一致的問題
編程語言引入鎖機(jī)制,對資源進(jìn)行鎖定

“鎖”是一個(gè)抽象概念
鎖放在對象頭中。
鎖中記錄了當(dāng)前對象被哪個(gè)線程所占用
Java對象:
對象頭、實(shí)例數(shù)據(jù)、對齊填充字節(jié)
對象頭存放對象的運(yùn)行時(shí)信息
對象頭包含
- MarkWork
- 存儲(chǔ)和當(dāng)前對象運(yùn)行時(shí)狀態(tài)有關(guān)的數(shù)據(jù)
- hashcode
- 鎖狀態(tài)標(biāo)志
- 指向鎖記錄的指針
- 偏向鎖id
- 鎖標(biāo)志位
- ClassPoint
- 指針
- 指向當(dāng)前對象類型所在方法去中斷類型數(shù)據(jù)

32bit

無鎖、偏向鎖、輕量級(jí)鎖、重量級(jí)鎖
synchronized 編譯后生成兩個(gè)字節(jié)碼指令
- monitorenter
- monitorexit
依賴此二指令實(shí)現(xiàn)線程同步
javac 編譯 javac *.java
javap 反編譯 javap -c *.class
得到可讀性較高的字節(jié)碼
synchronized同步機(jī)制圍繞Monitor展開


monitor是依賴操作系統(tǒng)的mutex lock來實(shí)現(xiàn)的
Java系統(tǒng)是對操作系統(tǒng)線程的映射
每當(dāng)掛起或者喚醒,都需要切換操作系統(tǒng)的內(nèi)核態(tài)——這是“重量級(jí)的操作”,對程序性能有嚴(yán)重影響。


synchronized是如何優(yōu)化的
(鎖)四種狀態(tài)是如何變化的?

“偏向”:monitor偏向于對指定的線程交出鎖

MarkWord中,當(dāng)鎖標(biāo)志位是01時(shí),判斷倒數(shù)第3個(gè)bit是否為1,
- 若為0,則非偏向鎖。
- 若為1,則當(dāng)前對象的鎖狀態(tài)為偏向鎖
- 若為偏向鎖,再讀取MarkWord前23bit,即對象的線程ID,根據(jù)線程ID,判斷是不是“老顧客”
- 若是: 則直接調(diào)用對象
- 若否:即多個(gè)對象同時(shí)競爭,則偏向鎖將升級(jí)為輕量鎖。
當(dāng)鎖狀態(tài)為“偏向鎖”時(shí),通過MarkWord的線程ID來找到占有該鎖的線程。
當(dāng)鎖狀態(tài)升級(jí)為輕量級(jí)鎖時(shí),如何判斷你線程與鎖的綁定關(guān)系
MarkWord前30bit改為指向棧中鎖記錄的指針

線程判別所標(biāo)志位是否為00輕量級(jí)鎖
若是,線程在自己的虛擬機(jī)棧中開辟Lock Record的空間
(Lock Record 是線程私有的)
Lock Record 存放對象的
- MarkWord副本
- owner指針
“線程通過CAS嘗試獲取鎖,一旦獲得將復(fù)制對象頭中的MarkWord道Lock Record中,并將LockRecord的owner指針指向該對象”

另一方面
對象的前30bit,生成一個(gè)指針,指向虛擬機(jī)棧中的Lock Record
如此邊雙向關(guān)聯(lián)了 markwork <-> 線程虛擬機(jī)棧
“實(shí)現(xiàn)了線程和對象鎖的綁定(他們互相知道了對方的存在)”

此時(shí)該對象已被鎖定,獲取道對象的線程就可以去執(zhí)行一些任務(wù)

此時(shí)如果有其他線程也想獲取這個(gè)對象
其他的線程將會(huì)“自旋”等待

“自旋”:一種輪詢,線程不斷地自我循環(huán),檢查目標(biāo)對象的鎖有沒有被釋放
- 若釋放,獲取之
- 若未釋放,則進(jìn)行下一輪循環(huán)
若對象鎖很快被釋放,相比OS掛起,自旋不需要進(jìn)行系統(tǒng)中斷和現(xiàn)場恢復(fù),效率更高。
自旋:相當(dāng)于CPU在空轉(zhuǎn)。長時(shí)間自旋,會(huì)浪費(fèi)CPU資源。

自適應(yīng)自旋:自旋的實(shí)踐不固定,
由兩個(gè)條件決定:
- 上一次在同一個(gè)鎖上的自旋時(shí)間
- 鎖狀態(tài)
“在同一個(gè)鎖上,當(dāng)前正在自選等待的線程,剛剛已經(jīng)成功獲得過鎖,但是鎖目前被其他線程占用著,那么虛擬機(jī)就會(huì)認(rèn)為這次自旋也很有可能會(huì)再次成功,進(jìn)而它將允許更長的自選時(shí)間”

一旦自旋等待的線程數(shù)量超過1個(gè),那么輕量級(jí)鎖將會(huì)升級(jí)為重量級(jí)鎖

若對象鎖狀態(tài)被標(biāo)記為重量級(jí)鎖,則需要通過Monitor來對線程進(jìn)行控制,此時(shí)將會(huì)完全鎖定資源,對線程的管控最為嚴(yán)格
(升級(jí)道重量鎖,等于變回了OS Mutex)

鎖、Java鎖、對象頭、MarkWord
synchronized -> monitor -> mutex lock
無鎖->偏向->輕量->重量
