【synchronized底层原理之4】锁的升级过程及比较
2019獨角獸企業重金招聘Python工程師標準>>>
參考
https://blog.csdn.net/zqz_zqz/article/details/70233767
https://monkeysayhi.github.io/2018/01/02/%E6%B5%85%E8%B0%88%E5%81%8F%E5%90%91%E9%94%81%E3%80%81%E8%BD%BB%E9%87%8F%E7%BA%A7%E9%94%81%E3%80%81%E9%87%8D%E9%87%8F%E7%BA%A7%E9%94%81/
偏向鎖獲取過程
輕量級鎖的加鎖過程
輕量級鎖的釋放過程
釋放鎖線程的視角
由輕量鎖切換到重量鎖,是發生在輕量鎖釋放鎖的期間,之前在獲取鎖的時候它拷貝了鎖對象頭的markword,在釋放鎖的時候如果它發現在它持有鎖的期間有其他線程來嘗試獲取鎖了,并且該線程對markword做了修改,兩者比對發現不一致,則切換到重量鎖。
因為重量級鎖被修改了,所以display mark word和原來的markword不一樣了。
怎么補救,就是進入mutex前,compare一下obj的markword狀態。確認該markword是否被其他線程持有。
此時如果線程已經釋放了markword,那么通過CAS后就可以直接進入線程,無需進入mutex,就這個作用。
嘗試獲取鎖線程的視角
如果線程嘗試獲取鎖的時候,輕量鎖正被其他線程占有,那么它就會修改markword,修改重量級鎖,表示該進入重量鎖了。
還有一個注意點:等待輕量鎖的線程不會阻塞,它會一直自旋等待鎖,并如上所說修改markword。
這就是自旋鎖,嘗試獲取鎖的線程,在沒有獲得鎖的時候,不被掛起,而轉而去執行一個空循環,即自旋。在若干個自旋后,如果還沒有獲得鎖,則才被掛起(進入阻塞狀態),獲得鎖,則執行代碼。
synchronized的執行過程
1. 檢測Mark Word里面是不是當前線程的ID,如果是,表示當前線程處于偏向鎖
2. 如果不是,則使用CAS將當前線程的ID替換Mard Word,如果成功則表示當前線程獲得偏向鎖,置偏向標志位1
3. 如果失敗,則說明發生競爭,撤銷偏向鎖,進而升級為輕量級鎖。
4. 當前線程使用CAS將對象頭的Mark Word替換為鎖記錄指針,如果成功,當前線程獲得鎖
5. 如果失敗,表示其他線程競爭鎖,當前線程便嘗試使用自旋來獲取鎖。
6. 如果自旋成功則依然處于輕量級狀態。
7. 如果自旋失敗,則升級為重量級鎖。
上面幾種鎖都是JVM自己內部實現,當執行synchronized同步塊的時候jvm會根據啟用的鎖和當前線程的爭用情況,決定如何執行同步操作。
在所有的鎖都啟用的情況下線程進入臨界區時會先去獲取偏向鎖,如果已經存在偏向鎖了,則會嘗試獲取輕量級鎖,啟用自旋鎖,如果自旋也沒有獲取到鎖,則使用重量級鎖,沒有獲取到鎖的線程阻塞掛起,直到持有鎖的線程執行完同步塊喚醒他們
偏向鎖是在無鎖爭用的情況下使用的,也就是同步開在當前線程沒有執行完之前,沒有其它線程會執行該同步塊,一旦有了第二個線程的爭用,偏向鎖就會升級為輕量級鎖,如果輕量級鎖自旋到達閾值后,沒有獲取到鎖,就會升級為重量級鎖。
如果線程爭用激烈,那么應該禁用偏向鎖。
偏向鎖、輕量級鎖、重量級鎖的比較
| ? | 重量級鎖 | 輕量級鎖 | 偏向鎖 |
| 適用場景 | 發生了鎖爭搶的情況:多條線程進入同步塊并爭用鎖 | 雖然很多線程,但是沒有沖突:多條線程進入同步塊,但是線程進入時間錯開因而并未爭搶鎖 | 自始至終只有一個線程:只有一個線程進入同步塊 |
| 本質 | 互斥同步 | CAS操作代替互斥同步 | 取消同步操作 |
| 優點 | 不會空耗CPU | 不會阻塞 | 不阻塞,執行效率高(只有第一次獲取偏向鎖時需要CAS操作,后面只是比對ThreadId) |
| 缺點 | 阻塞,上下文切換,重量級操作 | 長時間獲取不到鎖空耗CPU | 適用場景太局限。若競爭產生,會有額外的偏向鎖撤銷的消耗 |
不同的鎖有不同特點,每種鎖只有在其特定的場景下,才會有出色的表現,java中沒有哪種鎖能夠在所有情況下都能有出色的效率,引入這么多鎖的原因就是為了應對不同的情況。
轉載于:https://my.oschina.net/u/3866531/blog/2050750
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的【synchronized底层原理之4】锁的升级过程及比较的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【java】【mybatis】在使用my
- 下一篇: Redux专题:实用