Java鎖升級機(jī)制超詳細(xì)講解(附實例代碼)
引言
最近有個三年左右的兄弟面試java 被問到這樣一道經(jīng)典的八股文面試題: 你講講java里面的鎖升級? 他感覺回答的不是很好,然后回去找資料學(xué)習(xí)了一波,然后下面是他輸出的文章,希望對找工作的其他朋友也有些幫助。
1. 概述
Java 的鎖升級機(jī)制是 JVM 在 JDK 1.6 后引入的重要優(yōu)化策略,目的是在多線程環(huán)境下平衡 線程安全 與 性能開銷。通過動態(tài)調(diào)整鎖的復(fù)雜度,JVM 根據(jù)競爭強(qiáng)度逐步升級鎖的狀態(tài),避免在低競爭場景下使用高成本的重量級鎖。
2. 鎖類型及特點
鎖類型 | 適用場景 | 性能開銷 | 核心機(jī)制 |
---|---|---|---|
無鎖(Unlocked) | 無線程競爭 | 極低 | 直接通過 CAS 操作嘗試獲取鎖。 |
偏向鎖(Biased Locking) | 單線程重復(fù)訪問(無競爭) | 極低 | 對象頭記錄偏向線程 ID,后續(xù)同一線程無需競爭,直接獲取鎖。 |
輕量級鎖(Lightweight Lock) | 低競爭(多個線程交替訪問) | 中等 | 通過 CAS 自旋嘗試獲取鎖,避免操作系統(tǒng)級別的阻塞。 |
重量級鎖(Heavyweight Lock) | 高競爭(長時間阻塞或高并發(fā)) | 高 | 依賴操作系統(tǒng)互斥量(Mutex),線程被掛起并排隊等待。 |
3. 鎖升級的過程
鎖升級路徑為:無鎖 → 偏向鎖 → 輕量級鎖 → 重量級鎖,且 不可逆(只能升級,不能降級)。
3.1 無鎖 → 偏向鎖
- 觸發(fā)條件:第一個線程訪問同步代碼塊時。
- 過程:
JVM 通過 CAS 操作將對象頭的
Mark Word
標(biāo)記為偏向鎖。記錄當(dāng)前線程 ID 和偏向時間戳。
后續(xù)同一線程再次訪問時,直接通過比對線程 ID 獲取鎖(無需 CAS 操作)。
3.2 偏向鎖 → 輕量級鎖
- 觸發(fā)條件:第二個線程嘗試獲取同一鎖(出現(xiàn)競爭)。
- 過程:
偏向鎖失效,JVM 撤銷偏向鎖(可能觸發(fā) STW,Stop-The-World)。
線程通過自旋(Spin)和 CAS 操作嘗試獲取鎖。
若自旋成功,則升級為輕量級鎖;否則繼續(xù)自旋或升級為重量級鎖。
3.3 輕量級鎖 → 重量級鎖
- 觸發(fā)條件:
- 自旋次數(shù)超過閾值(默認(rèn) 10 次,可通過
-XX:PreBlockSpin
調(diào)整)。 - 多個線程同時競爭鎖(如第三個線程加入競爭)。
- 自旋次數(shù)超過閾值(默認(rèn) 10 次,可通過
- 過程:
JVM 將鎖升級為重量級鎖,對象頭指向監(jiān)視器(Monitor)。
線程進(jìn)入操作系統(tǒng)內(nèi)核態(tài)的阻塞隊列,等待調(diào)度器喚醒。
未獲取鎖的線程通過
ObjectMonitor
等待喚醒。
4. 鎖升級的優(yōu)缺點
4.1 優(yōu)點
- 減少無競爭場景的開銷:偏向鎖和輕量級鎖避免了頻繁的 CAS 和上下文切換。
- 動態(tài)適配競爭強(qiáng)度:在低競爭時保持高性能,在高競爭時保證線程安全。
4.2 缺點
- 偏向鎖撤銷開銷:當(dāng)其他線程競爭時,撤銷偏向鎖會導(dǎo)致 STW,影響性能。
- 重量級鎖的高開銷:在高競爭場景下,頻繁的線程阻塞/喚醒會顯著降低性能。
5. 鎖升級的優(yōu)化策略
5.1 減少鎖持有時間
- 優(yōu)化方向:縮短同步代碼塊的執(zhí)行時間,降低鎖的競爭概率。
- 示例:
// 不推薦:鎖持有時間過長 synchronized (lock) { // 復(fù)雜計算或 IO 操作 } // 推薦:僅在關(guān)鍵代碼塊加鎖 int result = doSomeComputation(); // 非同步操作 synchronized (lock) { sharedVariable = result; }
5.2 使用分段鎖(Segment Locking)
- 優(yōu)化方向:將一個大鎖拆分為多個小鎖,減少鎖的競爭范圍。
- 示例:
ConcurrentHashMap
使用分段鎖(JDK 8 后改為 CAS + synchronized)。
5.3 禁用偏向鎖
- 適用場景:頻繁切換線程的場景(如高并發(fā)服務(wù))。
- JVM 參數(shù):
-XX:-UseBiasedLocking # 禁用偏向鎖
5.4 調(diào)整自旋次數(shù)
- 適用場景:輕量級鎖的自旋可能因 CPU 空閑而浪費資源。
- JVM 參數(shù):
-XX:PreBlockSpin=5 # 設(shè)置自旋次數(shù)為 5
6. 代碼示例
public class LockUpgradeExample { private final Object lock = new Object(); public void performTask() { synchronized (lock) { // 同步代碼塊 } } public static void main(String[] args) { LockUpgradeExample example = new LockUpgradeExample(); Thread t1 = new Thread(example::performTask); Thread t2 = new Thread(example::performTask); t1.start(); // 初始為偏向鎖(t1) t2.start(); // 觸發(fā)偏向鎖撤銷,升級為輕量級鎖 } }
7. 關(guān)鍵 JVM 參數(shù)
參數(shù) | 作用 |
---|---|
-XX:+UseBiasedLocking | 開啟/關(guān)閉偏向鎖(默認(rèn)開啟,Java 15+ 默認(rèn)關(guān)閉)。 |
-XX:BiasedLockingStartupDelay=0 | 立即啟用偏向鎖(避免延遲)。 |
-XX:PreBlockSpin | 設(shè)置輕量級鎖自旋次數(shù)(默認(rèn) 10)。 |
-XX:-UseSpinning | 關(guān)閉自旋鎖(強(qiáng)制進(jìn)入重量級鎖)。 |
8. 總結(jié)
- 鎖升級是 JVM 自動管理的機(jī)制,開發(fā)者無需手動干預(yù),但理解其原理有助于優(yōu)化并發(fā)性能。
- 偏向鎖適合單線程場景,輕量級鎖適合低競爭場景,重量級鎖適合高競爭場景。
- 鎖升級不可逆,一旦升級到重量級鎖,后續(xù)操作將始終使用重量級鎖。
通過合理設(shè)計代碼(如減少鎖粒度、避免過早膨脹到重量級鎖),可以最大化 Java 的并發(fā)性能。
到此這篇關(guān)于Java鎖升級機(jī)制的文章就介紹到這了,更多相關(guān)Java鎖升級機(jī)制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決mybatis plus報錯com.microsoft.sqlserver.jdbc.SQLServerE
這篇文章主要介紹了解決mybatis plus報錯com.microsoft.sqlserver.jdbc.SQLServerException:必須執(zhí)行該語句才能獲得結(jié)果,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-05-05Spring中WebClient的創(chuàng)建和使用詳解
這篇文章主要介紹了Spring中WebClient的創(chuàng)建和使用詳解,在Spring5中,出現(xiàn)了Reactive響應(yīng)式編程思想,并且為網(wǎng)絡(luò)編程提供相關(guān)響應(yīng)式編程的支持,如提供了WebFlux,它是Spring提供的異步非阻塞的響應(yīng)式的網(wǎng)絡(luò)框架,需要的朋友可以參考下2023-11-11詳解Java 包掃描實現(xiàn)和應(yīng)用(Jar篇)
這篇文章主要介紹了詳解Java 包掃描實現(xiàn)和應(yīng)用(Jar篇),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-07-07Java使用jdbc連接實現(xiàn)對MySQL增刪改查操作的全過程
JDBC的全稱是Java?Database?Connectivity,即Java數(shù)據(jù)庫連接,它是一種可以執(zhí)行SQL語句的Java?API,下面這篇文章主要給大家介紹了關(guān)于Java使用jdbc連接實現(xiàn)對MySQL增刪改查操作的相關(guān)資料,需要的朋友可以參考下2023-03-03Java使用OTP動態(tài)口令(每分鐘變一次)進(jìn)行登錄認(rèn)證
這篇文章主要介紹了Java使用OTP動態(tài)口令(每分鐘變一次)進(jìn)行登錄認(rèn)證,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09Java 遍歷取出Map集合key-value數(shù)據(jù)的4種方法
這篇文章主要介紹了Java 遍歷取出Map集合key-value數(shù)據(jù)的4種方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09