一文帶你認(rèn)識(shí)Java中的鎖升級(jí)機(jī)制
一、鎖升級(jí)的本質(zhì):JVM 的“自適應(yīng)并發(fā)優(yōu)化”
核心思想:
“大多數(shù)鎖在運(yùn)行時(shí)沒(méi)有競(jìng)爭(zhēng),或競(jìng)爭(zhēng)時(shí)間極短。與其一開(kāi)始就使用重量級(jí)鎖,不如先嘗試更輕量的方式。”
鎖狀態(tài)演進(jìn)路徑(HotSpot 64位 JVM)
無(wú)鎖 → 偏向鎖(Biased Locking) → 輕量級(jí)鎖(Lightweight Locking) → 重量級(jí)鎖(Heavyweight Locking)
?? 關(guān)鍵特性:
- 單向升級(jí):鎖只能升級(jí),不能降級(jí)(除 GC 或顯式撤銷(xiāo))
- 基于對(duì)象頭 Mark Word 狀態(tài)切換
- 由 JVM 自動(dòng)決策,開(kāi)發(fā)者無(wú)需干預(yù)
二、深入原理 + 場(chǎng)景 + 代碼示例
1. 偏向鎖(Biased Locking)—— 單線程的極致優(yōu)化
底層原理
- 對(duì)象頭 Mark Word 中存儲(chǔ) 偏向線程 ID
- 同一線程再次進(jìn)入同步塊時(shí),僅比對(duì)線程 ID,無(wú)需 CAS
- 若對(duì)象未計(jì)算過(guò) hashCode,則用 hashcode 字段存儲(chǔ)線程 ID
Mark Word 結(jié)構(gòu)(偏向鎖狀態(tài)):
| unused:25 | thread_id:54 | epoch:2 | age:4 | biased_lock:1 | lock:01 |
典型場(chǎng)景
- Spring 單例 Bean 中的 synchronized 方法被同一線程反復(fù)調(diào)用
- 批處理任務(wù)(如定時(shí)對(duì)賬、數(shù)據(jù)清洗)
代碼示例 & 驗(yàn)證
public class SingleThreadDemo {
private final Object lock = new Object();
public void processBatch(List<Data> dataList) {
// 同一線程循環(huán)進(jìn)入,觸發(fā)偏向鎖
for (Data data : dataList) {
synchronized (lock) {
// 業(yè)務(wù)邏輯
handle(data);
}
}
}
// 注意:若在此處調(diào)用 lock.hashCode(),會(huì)禁用偏向鎖!
}? 性能優(yōu)勢(shì):接近無(wú)鎖,僅首次 CAS 設(shè)置偏向,后續(xù)零開(kāi)銷(xiāo)。
? 致命缺陷:偏向鎖撤銷(xiāo)需 Stop-The-World(STW),影響延遲敏感系統(tǒng)。
2. 輕量級(jí)鎖(Lightweight Locking)—— 低競(jìng)爭(zhēng)的優(yōu)雅退讓
底層原理
- 當(dāng)?shù)诙€(gè)線程嘗試獲取已被偏向的鎖時(shí),撤銷(xiāo)偏向鎖
- 線程在棧幀中創(chuàng)建 Lock Record
- 通過(guò) CAS 將對(duì)象頭 Mark Word 替換為指向 Lock Record 的指針
- 若 CAS 失敗,線程自旋重試(默認(rèn) 10 次),失敗則膨脹為重量級(jí)鎖
典型場(chǎng)景
- Web 請(qǐng)求處理(低并發(fā)、請(qǐng)求串行化)
- 日志寫(xiě)入(多線程交替寫(xiě),但不同時(shí))
代碼示例
@RestController
public class OrderController {
private final Object orderLock = new Object();
@PostMapping("/create")
public ResponseEntity<?> createOrder(@RequestBody OrderRequest req) {
synchronized (orderLock) { // 多個(gè)請(qǐng)求線程交替進(jìn)入
validate(req);
return orderService.create(req);
}
}
}? 優(yōu)勢(shì):避免線程阻塞/喚醒的系統(tǒng)調(diào)用開(kāi)銷(xiāo)(上下文切換成本約 1~10μs)
? 風(fēng)險(xiǎn):自旋浪費(fèi) CPU,在 CPU 密集型場(chǎng)景可能惡化性能
3. 重量級(jí)鎖(Heavyweight Locking)—— 高競(jìng)爭(zhēng)的最終保障
底層原理
- 基于 OS Mutex(互斥量) 實(shí)現(xiàn)
- 競(jìng)爭(zhēng)線程被掛起(
park()),放入 EntryList 隊(duì)列 - 持有鎖線程釋放時(shí),喚醒隊(duì)列中的一個(gè)線程(
unpark()) - 涉及 用戶態(tài) ↔ 內(nèi)核態(tài)切換,開(kāi)銷(xiāo)大(約 1~10μs)
典型場(chǎng)景
- 秒殺庫(kù)存扣減
- 全局配置更新
- 數(shù)據(jù)庫(kù)連接池管理
反面教材 vs 正確做法
// ? 反面:synchronized 導(dǎo)致重量級(jí)鎖競(jìng)爭(zhēng)
public class InventoryService {
private int stock = 100;
public synchronized boolean deduct() {
if (stock > 0) {
stock--; // 高并發(fā)下性能極差
return true;
}
return false;
}
}
// ? 正確:使用 LongAdder(分段無(wú)鎖)
public class OptimizedInventory {
private final LongAdder deducted = new LongAdder();
private final int totalStock = 100;
public boolean deduct() {
long current = deducted.sum();
if (current >= totalStock) return false;
deducted.increment(); // 無(wú)鎖,分段累加
return true;
}
}三、生產(chǎn)環(huán)境實(shí)戰(zhàn):如何應(yīng)用與調(diào)優(yōu)?
生產(chǎn)最佳實(shí)踐
| 場(chǎng)景 | 推薦方案 | 原理 |
|---|---|---|
| 微服務(wù)高并發(fā) | 關(guān)閉偏向鎖 | 避免頻繁撤銷(xiāo)導(dǎo)致 STW |
| 計(jì)數(shù)器/狀態(tài) | LongAdder / AtomicXXX | 分段無(wú)鎖,避免全局競(jìng)爭(zhēng) |
| 讀多寫(xiě)少 | StampedLock | 樂(lè)觀讀,提升吞吐 |
| 復(fù)雜同步邏輯 | ReentrantLock + Condition | 支持超時(shí)、中斷、公平鎖 |
JVM 參數(shù)調(diào)優(yōu)(生產(chǎn)常用)
# 關(guān)閉偏向鎖(推薦微服務(wù)場(chǎng)景) -XX:-UseBiasedLocking # 禁用偏向鎖延遲(啟動(dòng)即生效) -XX:BiasedLockingStartupDelay=0 # 監(jiān)控鎖統(tǒng)計(jì)(診斷用) -XX:+PrintBiasedLockingStatistics -XX:+UnlockDiagnosticVMOptions -XX:+PrintSafepointStatistics # 調(diào)整自旋次數(shù)(謹(jǐn)慎?。? -XX:PreBlockSpin=15
?? 真實(shí)案例:
某電商平臺(tái)在大促前發(fā)現(xiàn) GC Pause 異常升高,經(jīng) JFR 分析發(fā)現(xiàn) 偏向鎖撤銷(xiāo)頻繁觸發(fā) safepoint。關(guān)閉-XX:-UseBiasedLocking后,P99 延遲下降 40%。
四、優(yōu)缺點(diǎn)深度分析
| 鎖類(lèi)型 | 優(yōu)點(diǎn) | 缺點(diǎn) | 適用邊界 |
|---|---|---|---|
| 偏向鎖 | 單線程零開(kāi)銷(xiāo) | 撤銷(xiāo)需 STW,不適合多線程交替 | 單線程長(zhǎng)期持有(< 1% 生產(chǎn)場(chǎng)景) |
| 輕量級(jí)鎖 | 避免內(nèi)核切換 | 自旋浪費(fèi) CPU,僅適合短臨界區(qū) | 低競(jìng)爭(zhēng) + 臨界區(qū) < 10μs |
| 重量級(jí)鎖 | 強(qiáng)一致性保障 | 上下文切換開(kāi)銷(xiāo)大 | 高競(jìng)爭(zhēng)或長(zhǎng)臨界區(qū) |
?? 關(guān)鍵洞察:
鎖升級(jí)是 JVM 的“事后補(bǔ)救”機(jī)制。真正的高性能系統(tǒng)應(yīng)從架構(gòu)層面避免鎖競(jìng)爭(zhēng),而非依賴鎖優(yōu)化。
五、實(shí)際開(kāi)發(fā)選型策略 —— 高級(jí)工程師思維
決策樹(shù)(體現(xiàn)深度)

高級(jí)建議(面試加分項(xiàng))
- 優(yōu)先無(wú)鎖:使用
volatile+ CAS(如AtomicReference) - 縮小鎖粒度:只鎖臨界區(qū),而非整個(gè)方法
// Bad public synchronized void method() { /* ... */ } // Good public void method() { // 非臨界區(qū) prepare(); synchronized (this) { // 臨界區(qū) } } - 監(jiān)控鎖競(jìng)爭(zhēng):使用 Arthas、JFR 分析
# Arthas 查看 BLOCKED 線程 thread --state BLOCKED # JFR 記錄鎖事件 java -XX:StartFlightRecording=duration=60s,filename=lock.jfr ...
六、面試深度
Q1: 為什么現(xiàn)代微服務(wù)要關(guān)閉偏向鎖?
答:微服務(wù)使用線程池復(fù)用線程,多個(gè)請(qǐng)求線程交替訪問(wèn)同一對(duì)象,導(dǎo)致偏向鎖頻繁撤銷(xiāo)。而撤銷(xiāo)需觸發(fā) safepoint(STW),在高 QPS 下會(huì)顯著增加 P99 延遲。Kafka、Netty 等高性能框架均默認(rèn)關(guān)閉。
Q2: synchronized 和 ReentrantLock 在 JDK 17 下性能對(duì)比?
答:JDK 6+ 對(duì) synchronized 進(jìn)行了鎖消除、鎖粗化、鎖升級(jí)等優(yōu)化,在低競(jìng)爭(zhēng)場(chǎng)景性能優(yōu)于 ReentrantLock(后者始終走重量級(jí)路徑)。但 ReentrantLock 提供更多功能(超時(shí)、公平鎖、Condition),適用于復(fù)雜同步場(chǎng)景。
Q3: 如何證明鎖升級(jí)發(fā)生了?
答:可通過(guò)以下方式驗(yàn)證:
- 使用 JOL(Java Object Layout) 打印對(duì)象頭
- 開(kāi)啟 JVM 參數(shù)
-XX:+PrintBiasedLockingStatistics- 使用 JFR(Java Flight Recorder) 記錄 Monitor Blocked 事件
總結(jié):高級(jí)工程師的并發(fā)觀
鎖升級(jí)是 JVM 的“安全網(wǎng)”,但真正的高手從不依賴安全網(wǎng)。應(yīng)該:
- 理解原理:知道 JVM 如何優(yōu)化,也知其局限
- 規(guī)避競(jìng)爭(zhēng):通過(guò)無(wú)狀態(tài)設(shè)計(jì)、分片、異步化減少鎖需求
- 工具驅(qū)動(dòng):用 JFR、Arthas 驗(yàn)證假設(shè),而非猜測(cè)
- 權(quán)衡取舍:在一致性、性能、復(fù)雜度間找到平衡點(diǎn)
“最好的并發(fā)控制,是沒(méi)有并發(fā)控制。”
—— 通過(guò)架構(gòu)設(shè)計(jì)從根本上消滅鎖,才是高級(jí)工程師的終極追求。
到此這篇關(guān)于Java中鎖升級(jí)機(jī)制的文章就介紹到這了,更多相關(guān)Java鎖升級(jí)機(jī)制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JAVA 多線程之信號(hào)量(Semaphore)實(shí)例詳解
這篇文章主要介紹了JAVA 多線程之信號(hào)量(Semaphore)實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-01-01
SpringBoot集成Access?DB實(shí)現(xiàn)數(shù)據(jù)導(dǎo)入和解析
microsoft?office?access是由微軟發(fā)布的關(guān)聯(lián)式數(shù)據(jù)庫(kù)管理系統(tǒng),它結(jié)合了?microsoft?jet?database?engine?和?圖形用戶界面兩項(xiàng)特點(diǎn),是一種關(guān)系數(shù)據(jù)庫(kù)工具,本文給大家介紹了SpringBoot集成Access?DB實(shí)現(xiàn)數(shù)據(jù)導(dǎo)入和解析,需要的朋友可以參考下2024-11-11
Spring Boot中數(shù)據(jù)庫(kù)操作Druid和HikariDataSource的詳細(xì)過(guò)程
這篇文章主要介紹了Spring Boot中數(shù)據(jù)庫(kù)操作Druid和HikariDataSource的詳細(xì)過(guò)程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-06-06
在java List中進(jìn)行模糊查詢的實(shí)現(xiàn)方法
下面小編就為大家?guī)?lái)一篇在java List中進(jìn)行模糊查詢的實(shí)現(xiàn)方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-11-11
springboot使JUL實(shí)現(xiàn)日志管理功能
這篇文章主要介紹了springboot使JUL實(shí)現(xiàn)日志管理功能,本文分步驟給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09
一文詳解Spring任務(wù)執(zhí)行和調(diào)度(小結(jié))
這篇文章主要介紹了一文詳解Spring任務(wù)執(zhí)行和調(diào)度(小結(jié)),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08
Java中時(shí)間戳的獲取和轉(zhuǎn)換的示例分析
這篇文章主要介紹了Java中時(shí)間戳的獲取和轉(zhuǎn)換的示例分析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07

