對象頭:synchronized用的鎖是存在Java對象頭裡的。Java對象頭裡的Mark Word裡預設存儲對象的HashCode、分代年齡和鎖标記位,主要用來表示對象的線程鎖狀态。
棧幀:也叫過程活動記錄,是編譯器用來實作過程/函數調用的一種資料結構。
鎖會随着線程的競争情況逐漸更新,偏向鎖 => 輕量級鎖 => 重量級鎖 。鎖可以更新但是不能降級。更新的目的是為了提高獲得鎖和釋放鎖的效率。
偏向鎖
當一個線程通路同步塊擷取鎖時,會在對象頭和棧幀的鎖記錄裡存儲鎖偏向的線程ID,以後該線程再進入和推出同步塊時不需要進行CAS(比較和交換,下次詳細記錄一下)操作來加鎖和解鎖。
通過判斷對象頭的Mark Word裡面是否存有指向目前線程的偏向鎖來決定是否需要使用CAS競争鎖。
适用場景:偏向鎖隻适合大部分鎖沒有被競争的系統中,如果系統中存在大量被争用的鎖時,會導緻持有鎖的線程不斷切換,這時可以考慮關閉偏向鎖。
可以通過JVM參數關閉偏向鎖:
-XX:- UseBiasedLocking=false
,程式預設會進入輕量級鎖狀态。
輕量級鎖
阻塞線程需要cpu從使用者态轉到核心态,代價比較大。而且可能會出現剛阻塞不久,鎖就被釋放的情況。為了縮小性能消耗,引入輕量級鎖。
加鎖
線程在執行同步塊之前,JVM會先在目前線程的棧桢中建立用于存儲鎖記錄的空間,并将對象頭中的Mark Word複制到鎖記錄中,官方稱為Displaced Mark Word。然後線程嘗試使用 CAS将對象頭中的Mark Word替換為指向鎖記錄的指針。如果成功,目前線程獲得鎖,如果失敗,表示其他線程競争鎖,目前線程便嘗試使用自旋來擷取鎖。
解鎖
輕量級解鎖時,會使用原子的CAS操作将Displaced Mark Word替換回到對象頭,如果成功,則表示沒有競争發生。如果失敗,表示目前鎖存在競争,鎖就會膨脹成重量級鎖。
适用場景:少量線程交替擷取鎖,同步塊執行速度非常快的場景。追求響應時間。
優缺點對比
偏向鎖
優:加鎖/解鎖不需要額外的消耗
缺:有競争時,會有額外的撤銷鎖或更新鎖的消耗
輕量級鎖
優:競争的線程不會被阻塞(采用自旋),提高了程式的響應速度
缺:始終得不到鎖的線程一直自旋會消耗cpu,造成cpu浪費(自旋好像就是無實際意義的循環,可以設定一個自旋等待的最大時間)
重量級鎖
優:線程競争不使用自旋,線程競争鎖失敗後會阻塞,cpu的消耗會減少,增大了資料的吞吐量
缺:線程阻塞,響應速度慢
适用場景:大量線程同時競争鎖,追求吞吐量。
以下場景可異步化
1、列印日志
2、郵件發送 檔案上傳 定時任務同步資料
3、下發配置
4、部署服務
5、話費充值
6、微服務健康度掃描