之前我們講了synchronized的原理(《且聽我一個故事講透一個鎖原理之synchronized》 ),以及其中的偏向鎖(《偏向鎖到底是怎麼回事啊啊啊啊》),送佛送到西,這次我們來說一說輕量級鎖吧。
友情提醒:上面兩篇沒有看過的看官,請先閱讀上面兩篇之後,再讀此篇,這樣效果會更佳哦~
![輕量級鎖到底是怎麼回事啊啊啊啊](http://p2.ttnews.xyz/loading.gif)
開局一張圖。
看一張大圖
![輕量級鎖到底是怎麼回事啊啊啊啊](http://p2.ttnews.xyz/loading.gif)
輕量級鎖
流程講解
當JVM關閉了偏向鎖模式,對象在創建的時候,Mark Word中存儲的是hash值,年代,是否偏向鎖標誌位為0,標誌位是01。即一個無鎖不可偏向狀態。
輕量級鎖邏輯:
1.當線程訪問同步塊,先判斷鎖狀態標誌位,如果是00,則說明是輕量級鎖,JVM會先在當前線程棧幀中分配Lock Record空間;
2.將鎖對象頭中的Mark Word拷貝到當前線程的Lock Record中,稱為Displaced Mark Word,然後使用CAS,將對象頭中的Mark Word修改為指向當前線程棧中Lock Record的指針(如圖)。如果成功,則獲取輕量級鎖,執行同步塊中的代碼,如果失敗,則進行自旋競爭鎖,自旋達到一定的次數如果依舊沒有獲取到鎖,則升級為重量級鎖(因為自旋會消耗CPU,為了避免無用的自旋,一旦鎖升級為重量級鎖,就不會恢復到輕量級鎖,自旋的線程會被掛起阻塞住);
CAS操作之前堆棧與對象的狀態
CAS操作之後堆棧與對象的狀態
3.執行完同步代碼塊代碼,退出同步代碼塊,使用CAS開始輕量級鎖解鎖,解鎖的條件需要滿足以下兩個:
1)對象頭Mark Word中鎖記錄指針是否依舊指向當前線程Lock Record
2)拷貝在當前線程Lock Record的Mark Word信息是否與對象頭中的Mark Word一致
4.如果滿足,則成功釋放鎖;
5.如果不滿足,則釋放鎖,喚醒被掛起阻塞的線程,開始重量級鎖的競爭。
注:當超過自旋閾值,競爭的線程就會把鎖對象Mark Word指向重量級鎖,導致Mark Word中的值發生了變化,當原持有輕量級鎖的線程執行完畢,嘗試通過CAS釋放鎖時,因為Mark Word已經指向重鎖,不再是指向當前線程Lock Record的指針,於是解鎖失敗,這時原持有輕量級鎖的線程就會知道鎖已經升級為重量級鎖。
偏向鎖升級為輕量級鎖:
1.先在原持有偏向鎖的線程棧幀中分配Lock Record;
2.將對象頭Mark Word拷貝到原持有偏向鎖的線程Lock Record中,然後使用CAS,將對象頭中的Mark Word修改為指向當前線程棧中Lock Record的指針。將原持有偏向鎖的線程升級為持有偏向鎖的線程;
3.喚醒線程,從安全點繼續執行,執行完畢解鎖。
輕量級鎖的重入計數
我們看個demo,在該demo中重複3次獲得鎖,
synchronized(obj){
synchronized(obj){
synchronized(obj){
}
}
}
假設鎖的狀態是輕量級鎖,如圖反應了Mark Word和線程棧中Lock Record的關係,右邊線程棧中包含3個指向當前鎖對象的Lock Record。其中棧中最高位的Lock Record為第一次獲取鎖時分配的,其中Displaced Mark word為鎖對象加鎖前的Mark Word,而之後的鎖重入,則會在線程棧中分配一個Displaced Mark word為null的Lock Record,用來重入計數。
重入
每次釋放鎖的時候則會刪除對應的Lock Record。
這就是輕量級鎖的實現邏輯,相對於偏向鎖來說,邏輯會稍微簡單一些。
如果覺得學習到了,歡迎轉發加關注,那是我分享的動力呀~
閱讀更多 IT一刻鐘 的文章