monitor(監視器/管程)
java對象分三部分,
- 對象頭
- 數據實例
- 填充
對象頭分為
- 普通對象- markword(32bit)/klass word(32bit)(指向對應的class對象)
- 數組對象-多一個array length(32bit)數組長度
markword的結構
- hashcode(25) age(4) biased_lock:0(代表是否是偏向鎖) | 01 (代表加鎖狀態) normal狀態(正常狀態)
- thread:54(線程id) epoch:2 age(4) biased_lock:1(代表是偏向鎖)|01(代表加鎖狀態) biased(偏向鎖)
- ptr_to_heavyweight_monitor(30,指向monitor的指針) |10(代表加了重量級 heavyweight lock(重量級鎖定)
- ptr_to_lock_record:30 (30位代表鎖記錄的地址) |00(代表加輕鎖) LightWeight Lock(輕量級鎖定)
monitor是操作系統提供的,內部結構
-
waitset
-
entrylist(等待隊列)
-
owner(monitor擁有者)
`具體步驟1是字節碼中的monitorenter操作指令 步驟4是字節碼中monitorexit指令
重量級鎖的加鎖流程:
1. thread0執行synchronize代碼的時候,synchronized(obj)的obj對象的markword中ptr_to_heavyweight_monitor會指向一個monitor對象,monitor的owner是thread0
2. thread1執行到synchronized代碼時,發現obj的markword指向了一個monitor並且owner有人了,這時會進入entrylist進行blocked
3. thread2也一樣
4. thread0執行完同步代碼退出synchronized,把obj markword里的數據還原比如hashcode age啥的,這些數據是存在monitor對象中的,然后喚醒entrylist的thread1和thread2的blocked線程,兩個線程去搶owner
5. 如果出現異常,jvm會自動釋放鎖 執行第4步
`
因為獲取monitor是系統指令,需要從用戶態轉為內核態,出現上下文切換啥的,比較耗費資源,所以java1.6版本對synchronized進行了優化
-
輕量級鎖/使用場景是一個對象雖然有多個線程訪問,但是不出現競爭,這時使用輕量級鎖來優化
加鎖流程:- 創建鎖記錄(lock record)對象(內部存儲鎖定對象的mark word,還有一個對象指針記錄)
- 讓鎖記錄中的object reference指向鎖對象,將鎖對象的mark word值存入鎖記錄(這時鎖記錄記錄的是hashcode age 01無鎖狀態,obj鎖對象對象頭中存儲的是鎖記錄地址和狀態00 鎖添加成功
- 如果obj鎖對象中的狀態已經是00鎖記錄存儲的是其他線程這時加鎖失敗
cas替換失敗了有兩種情況
1.如果其他線程已經持有了該object的輕量級鎖,這時出現了競爭,進行鎖膨脹
2.如果是線程自己執行了鎖重入,添加一條lock record在鎖持有線程的棧幀中 指向obj鎖對象 並進行cas操作(一定失敗),作為重入的計數
釋放鎖流程:
- 退出同步代碼塊時,如果有鎖記錄為null的情況表示有重入,重置鎖記錄表示重入計數減一
- 當鎖記錄不為null,這時使用cas操作恢復markword給obj鎖對象的對象頭
兩種情況: - 恢復成功 解鎖成功
- 失敗說明輕量級鎖進行了鎖膨脹,這時需要執行重量級鎖的解鎖流程
鎖膨脹:
輕量級鎖出現了競爭(在嘗試加輕量級鎖的過程中,其他線程為此對象已經加上了輕量級鎖),這時需要進行鎖膨脹,升級為重量級鎖
thread0持有着輕量級鎖,thread1進來加輕量級鎖,出現競爭這時thread1加鎖失敗進行鎖膨脹,
1. thread1為obj對象申請monitor對象,並將obj鎖對象的mark word中指向monitor,thread1自己進入entrylist進行blocked
2. 當thread0執行完同步代碼塊,解鎖的時候發現鎖的標識變了,mark word也不是指向棧幀中的鎖記錄了,這時執行重量級鎖的解鎖流程
自旋優化(適用於多核處理器)
重量級鎖出現競爭的時候,使用自旋來優化
偏向鎖(優先加偏向鎖)
輕量級鎖在加鎖成功后,鎖重入的時候會再執行一次cas的操作(雖然必定失敗),這樣會浪費資源
偏向鎖的加鎖流程:
第一次使用cas操作將線程的id存入obj對象頭的markword,之后發現這個id是自己就說明沒有競爭,不用重新cas操作,只要不發生競爭,這個鎖就歸這個線程所有
如果調用對象的hashcode,會撤銷obj鎖對象的偏向狀態
當申請obj對象鎖的線程是不偏向的那個線程的時候(這時沒有競爭),會升級為輕量級鎖
批量重偏向
當偏向鎖撤銷超過20次,重偏向給加鎖的線程
批量撤銷
當偏向鎖撤銷超過40次,整個類的所有對象都變為不可偏向的
鎖消除
jit及時編譯器在運行時會優化掉