Synchronized偏向鎖和輕量級鎖的升級


原文:https://blog.csdn.net/tongdanping/article/details/79647337

 

鎖的優化
1、鎖升級
鎖的4中狀態:無鎖狀態、偏向鎖狀態、輕量級鎖狀態、重量級鎖狀態(級別從低到高)

(1)偏向鎖:

為什么要引入偏向鎖?

因為經過HotSpot的作者大量的研究發現,大多數時候是不存在鎖競爭的,常常是一個線程多次獲得同一個鎖,因此如果每次都要競爭鎖會增大很多沒有必要付出的代價,為了降低獲取鎖的代價,才引入的偏向鎖。

偏向鎖的升級

當線程1訪問代碼塊並獲取鎖對象時,會在java對象頭和棧幀中記錄偏向的鎖的threadID,因為偏向鎖不會主動釋放鎖,因此以后線程1再次獲取鎖的時候,需要比較當前線程的threadID和Java對象頭中的threadID是否一致,如果一致(還是線程1獲取鎖對象),則無需使用CAS來加鎖、解鎖;如果不一致(其他線程,如線程2要競爭鎖對象,而偏向鎖不會主動釋放因此還是存儲的線程1的threadID),那么需要查看Java對象頭中記錄的線程1是否存活,如果沒有存活,那么鎖對象被重置為無鎖狀態,其它線程(線程2)可以競爭將其設置為偏向鎖;如果存活,那么立刻查找該線程(線程1)的棧幀信息,如果還是需要繼續持有這個鎖對象,那么暫停當前線程1,撤銷偏向鎖,升級為輕量級鎖,如果線程1 不再使用該鎖對象,那么將鎖對象狀態設為無鎖狀態,重新偏向新的線程。

偏向鎖的取消:

偏向鎖是默認開啟的,而且開始時間一般是比應用程序啟動慢幾秒,如果不想有這個延遲,那么可以使用-XX:BiasedLockingStartUpDelay=0;

如果不想要偏向鎖,那么可以通過-XX:-UseBiasedLocking = false來設置;

(2)輕量級鎖

為什么要引入輕量級鎖?

輕量級鎖考慮的是競爭鎖對象的線程不多,而且線程持有鎖的時間也不長的情景。因為阻塞線程需要CPU從用戶態轉到內核態,代價較大,如果剛剛阻塞不久這個鎖就被釋放了,那這個代價就有點得不償失了,因此這個時候就干脆不阻塞這個線程,讓它自旋這等待鎖釋放。

輕量級鎖什么時候升級為重量級鎖?

線程1獲取輕量級鎖時會先把鎖對象的對象頭MarkWord復制一份到線程1的棧幀中創建的用於存儲鎖記錄的空間(稱為DisplacedMarkWord),然后使用CAS把對象頭中的內容替換為線程1存儲的鎖記錄(DisplacedMarkWord)的地址;

如果在線程1復制對象頭的同時(在線程1CAS之前),線程2也准備獲取鎖,復制了對象頭到線程2的鎖記錄空間中,但是在線程2CAS的時候,發現線程1已經把對象頭換了,線程2的CAS失敗,那么線程2就嘗試使用自旋鎖來等待線程1釋放鎖。

但是如果自旋的時間太長也不行,因為自旋是要消耗CPU的,因此自旋的次數是有限制的,比如10次或者100次,如果自旋次數到了線程1還沒有釋放鎖,或者線程1還在執行,線程2還在自旋等待,這時又有一個線程3過來競爭這個鎖對象,那么這個時候輕量級鎖就會膨脹為重量級鎖。重量級鎖把除了擁有鎖的線程都阻塞,防止CPU空轉。

 

*注意:為了避免無用的自旋,輕量級鎖一旦膨脹為重量級鎖就不會再降級為輕量級鎖了;偏向鎖升級為輕量級鎖也不能再降級為偏向鎖。一句話就是鎖可以升級不可以降級,但是偏向鎖狀態可以被重置為無鎖狀態。

(3)這幾種鎖的優缺點(偏向鎖、輕量級鎖、重量級鎖)

 

2、鎖粗化
按理來說,同步塊的作用范圍應該盡可能小,僅在共享數據的實際作用域中才進行同步,這樣做的目的是為了使需要同步的操作數量盡可能縮小,縮短阻塞時間,如果存在鎖競爭,那么等待鎖的線程也能盡快拿到鎖。 
但是加鎖解鎖也需要消耗資源,如果存在一系列的連續加鎖解鎖操作,可能會導致不必要的性能損耗。 
鎖粗化就是將多個連續的加鎖、解鎖操作連接在一起,擴展成一個范圍更大的鎖,避免頻繁的加鎖解鎖操作。

3、鎖消除
Java虛擬機在JIT編譯時(可以簡單理解為當某段代碼即將第一次被執行時進行編譯,又稱即時編譯),通過對運行上下文的掃描,經過逃逸分析,去除不可能存在共享資源競爭的鎖,通過這種方式消除沒有必要的鎖,可以節省毫無意義的請求鎖時間
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM