Monitor
每一個java對象都可以關聯一個Monitor對象,如果使用Synchronized給對象加鎖(重量級)之后,該對象的markword中就設置了指向Monitor的指針
- 剛開始Monitor中的owner為null
- 當Thread-1執行Synchronized(obj)之后會把owner設置為Thread-1,只能有一個owner,把obj中的mark word保管起來
- 在Thread-1上鎖中,其他線程執行Synchronized(obj),都會放到Monitor中EntryList中,線程置為阻塞狀態
- 當Thread-1執行完代碼快,喚醒EntryList中的線程,進行搶鎖(非公平的)。
Synchronized原理
Synchronized只有鎖同一個對象才能保證加鎖成功。
class Test{
public synchronized void test() {
}
}
等價於
class Test{
public void test() {
synchronized(this) {
}
}
}
class Test{
public synchronized static void test() {
}
}
等價於
class Test{
public static void test() {
synchronized(Test.class) {
}
}
}
Synchronized自從jdk 1.5后進行了優化,會有鎖升級的過程。
輕量級鎖
輕量級鎖的使用場景:如果一個對象雖然有多線程要加鎖,但加鎖的時間是錯開的(也就是沒有競爭),那么可以使用輕量級鎖來優化。
- 首先線程的棧幀會創建一個鎖記錄(lock record)對象,內部存儲lock record地址和指向鎖定對象的指針(Object reference)。
- 讓鎖記錄中的Object reference指向鎖定對象obj,並嘗試cas交換obj中的mark word
- 如果cas成功,obj中的mark word被替換成(lock record 地址 00)的形式,而棧幀中的lock record變為obj中mark word 的值(例如,hashcode,分代年齡)
- 如果cas失敗分為兩種情況
- 如果別的線程持有obj的輕量級鎖,這就表明有競爭,進入鎖升級過程
- 如果是自己執行了Synchronized重入,那么就再加一條lock record作為重入的計數
- 退出Synchronized代碼塊時,如果有取值為null的鎖記錄lock record,代表有重入,這時重置鎖記錄,表示重入計數減一
- 退出Synchronized代碼塊時lock record取值不為null,就cas將obj的markword還原。
- cas成功,解鎖成功
- 失敗 說明輕量級鎖進行了所升級,升級到了重量級鎖,進入重量級鎖流程。
重量級鎖
重量級鎖就用上述 Monitor執行過程。
-優化重量級鎖
重量級鎖競爭的時候,還可以使用自旋來進行優化,如果當前線程自旋成功(即這時候持鎖線程已經退出了同步塊,釋放了鎖),這時當前線程就可以避免阻塞。
Threa-1上鎖后,Thread-2這時候獲取Monitor,先自旋重試,而不是直接進入到EntryList中。自旋多次還不能獲取鎖,就進入到EntryList變為阻塞狀態。
偏向鎖
jdk1.5以后,偏向鎖是默認的,Thread-1第一次獲取鎖時會cas將obj中的markword變為thread-1的線程ID。那么下回Thread-1在獲取瑣時,發現markword中的id是他自己,就不用cas操作了,大大降低了延遲。但是偏向鎖沒有其他地方存放hashcode(輕量級鎖是保存在線程的棧幀中,重量級鎖是保存在monitor中),當用戶調用hashcode會導致偏向鎖被撤銷。