1 背景
在https://blog.csdn.net/qq_33996921/article/details/106629770這篇文章《Synchronized關鍵字和鎖升級(偏向鎖、輕量級鎖、重量級鎖)》中講解了Synchronized使用原理,及鎖的升級,在翻閱資料以及看別的博客中一些讀者遇到的疑問,本文主要針對在鎖升級中的一些問題進行解釋。
2 鎖升級的流程圖
先從《併發編程的藝術》這本書中摘抄了兩個鎖升級的圖:
1、偏向鎖的獲得和撤銷流程
2、輕量級鎖及膨脹流程
以上鎖的過程不做詳細的贅述,在上一篇提到的文章中已做了詳細描述,下面針對一些疑問做相應的解答。
3、鎖對象頭
對象頭的信息:
對象 MarkWord 狀態以不同方式轉換的過程如下:
翻譯一下:
4 鎖升級中的問題
1、問題當線程A 獲取了偏向鎖,此線程B進來了,走到了同步代碼塊前,此時是的鎖是如何變化的,看到文章中:線程B CAS
失敗會發起偏向鎖撤銷。 此處是線程A 升級爲輕量級鎖,還是重新爭搶? 如果是重新爭搶,失敗的線程會怎樣?
答:線程A修改
MarkWord中的信息,修改鎖的標識爲00,升級爲輕量級鎖;A已經持有偏向鎖,從偏向鎖升級爲輕量級鎖,是不需要再去重新爭搶的,線程B會自旋嘗試獲取輕量級鎖;
2、線程A、線程B同時到來,是不是直接就走輕量級鎖,而跳過偏向鎖,此是爭搶失敗的線程,發生自旋操作,如果自旋失敗,我看文章是升級到重量級鎖,那麼是線程A、B同時升級到重量級鎖的爭搶嗎?
答:走不走偏向鎖是跟開不開啓偏向鎖有關的,可以用idea進行測試,加入參數
-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0AB線程中,A線程先持有鎖,那麼B自旋失敗後就會把鎖膨脹成重量級鎖,這時候A就是持有了重量級鎖,B就是處於阻塞隊列了(被放在
_entryList裏),等A釋放後,B纔有機會獲取到鎖;
3、 jvm 是不是會自行根據當前併發,直接選擇是採用偏向鎖、輕量級、重量級,還是必須是從偏向鎖過渡到重量級鎖?
答:偏向鎖默認是不開啓的,如果要開啓可以用上面第(2)說的方法,不開啓就默認先獲取到輕量級鎖,然後再升級到重量級鎖,這其實是對鎖的一種優化。因爲加鎖、釋放鎖的成本,要比自旋等待獲取鎖的成本高很多。
4、ThreadA在獲得輕量級鎖後,其RecordLock裏存儲着鎖對象頭的Mark word信息,將鎖對象的Mark Word更新爲指向Lock Record的指針,在ThreadB自旋失敗後,進行鎖膨脹,修改爲重量級鎖,這裏有三個問題:
1)ThreadB修改爲重量級鎖,是修改的 Lock Record中存儲的Mark word的信息麼?
2)此時鎖對象的Mark Word指向的Lock Record的指針是 ThreadB的麼?
3)在ThreadB將鎖膨脹爲重量級鎖之後,正在執行中的ThreadA怎麼處理?
答:
1)線程B膨脹爲重量級鎖,是基於Monitor了,不是再修改markword;
2)所以第二個問題不存在;
3)鎖膨脹爲重量級鎖之後,ThreadA繼續運行,直到運行結束。
5、線程B多次自旋失敗後,將鎖升級爲重量級鎖之後,線程A就直接擁有了這把重量級鎖,然後Monitor對象中_owner存儲的是線程A的地址指針,而線程B則加入到__EntryList隊列中進行阻塞,等待被喚醒,那此時,鎖對象裏的對象頭裏的Mark Word信息是不會改變的,仍然存儲的是A的指針,是等到線程A釋放鎖的時候再去將Mark Word裏的鎖標識進行修改麼(修改爲重量級鎖10)
答:Mark word存儲的不再是輕量級鎖中指向Lock Record的指針,升級爲重量級鎖之後存儲的就是Monitor了。