java鎖升級過程

java中對象鎖有4種狀態:(級別從低到高)
1.無鎖狀態
2.偏向鎖狀態
3.輕量級鎖狀態
4.重量級鎖狀態

對象頭分兩部分信息,第一部分用於存儲哈希碼、GC分代年齡等,這部分數據被稱爲"Mark Word"。在32位的HotSpot虛擬機中對象未被鎖定的狀態下,Mark Word的32bit空間中的25bit用於存儲對象哈希碼,4bit用於存儲對象分代年齡,2bit用於存儲鎖標誌位,1bit固定爲0,在其他狀態(輕量級鎖定、重量級鎖定、GC標記、可偏向)下對象的存儲內容見下表:
在這裏插入圖片描述
鎖升級的方向是:無鎖——>偏向鎖——>輕量級鎖——>重量級鎖,並且膨脹方向不可逆。

1.偏向鎖
偏向鎖是JDK6中引入的一項鎖優化,大多數情況下,鎖不僅不存在多線程競爭,而且總是由同一線程多次獲得,爲了讓線程獲得鎖的代價更低而引入了偏向鎖。
偏向鎖會偏向於第一個獲得它的線程,如果在接下來的執行過程中,該鎖沒有被其他的線程獲取,則持有偏向鎖的線程將永遠不需要同步。

2.輕量級鎖
如果明顯存在其它線程申請鎖,那麼偏向鎖將很快升級爲輕量級鎖。

3.自旋鎖
自旋鎖原理非常簡單,如果持有鎖的線程能在很短時間內釋放鎖資源,那麼那些等待競爭鎖的線程就不需要做內核態和用戶態之間的切換進入阻塞掛起狀態,它們只需要等一等(自旋),等持有鎖的線程釋放鎖後即可立即獲取鎖,這樣就避免用戶線程和內核的切換的消耗。

4.重量級鎖
指的是原始的Synchronized的實現,重量級鎖的特點:其他線程試圖獲取鎖時,都會被阻塞,只有持有鎖的線程釋放鎖之後纔會喚醒這些線程。

鎖升級場景:

場景1: 程序不會有鎖的競爭。
那麼這種情況我們不需要加鎖,所以這種情況下對象鎖狀態爲無鎖。

場景2: 經常只有某一個線程來加鎖。
加鎖過程:也許獲取鎖的經常爲同一個線程,這種情況下爲了避免加鎖造成的性能開銷,所以並不會加實際意義上的鎖,偏向鎖的執行流程如下:
1、線程首先檢查該對象頭的線程ID是否爲當前線程;
2、A:如果對象頭的線程ID和當前線程ID一直,則直接執行代碼;B:如果不是當前線程ID則使用CAS方式替換對象頭中的線程ID,如果使用CAS替換不成功則說明有線程正在執行,存在鎖的競爭,這時需要撤銷偏向鎖,升級爲輕量級鎖。
3、如果CAS替換成功,則把對象頭的線程ID改爲自己的線程ID,然後執行代碼。
4、執行代碼完成之後釋放鎖,把對象頭的線程ID修改爲空。

場景3: 有線程來參與鎖的競爭,但是獲取鎖的衝突時間很短。
當開始有鎖的衝突了,那麼偏向鎖就會升級到輕量級鎖;線程獲取鎖出現衝突時,線程必須做出決定是繼續在這裏等,還是回家等別人打電話通知,而輕量級鎖的路基就是採用繼續在這裏等的方式,當發現有鎖衝突,線程首先會使用自旋的方式循環在這裏獲取鎖,因爲使用自旋的方式非常消耗CPU,當一定時間內通過自旋的方式無法獲取到鎖的話,那麼鎖就開始升級爲重量級鎖了。

場景4: 有大量的線程參與鎖的競爭,衝突性很高。
我們知道當獲取鎖衝突多,時間越長的時候,我們的線程肯定無法繼續在這裏死等了,所以只好先休息,然後等前面獲取鎖的線程釋放了鎖之後再開啓下一輪的鎖競爭,而這種形式就是我們的重量級鎖。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章