Java鎖升級(jí)機(jī)制超詳細(xì)講解(附實(shí)例代碼)
引言
最近有個(gè)三年左右的兄弟面試java 被問(wèn)到這樣一道經(jīng)典的八股文面試題: 你講講java里面的鎖升級(jí)? 他感覺(jué)回答的不是很好,然后回去找資料學(xué)習(xí)了一波,然后下面是他輸出的文章,希望對(duì)找工作的其他朋友也有些幫助。
1. 概述
Java 的鎖升級(jí)機(jī)制是 JVM 在 JDK 1.6 后引入的重要優(yōu)化策略,目的是在多線程環(huán)境下平衡 線程安全 與 性能開(kāi)銷。通過(guò)動(dòng)態(tài)調(diào)整鎖的復(fù)雜度,JVM 根據(jù)競(jìng)爭(zhēng)強(qiáng)度逐步升級(jí)鎖的狀態(tài),避免在低競(jìng)爭(zhēng)場(chǎng)景下使用高成本的重量級(jí)鎖。
2. 鎖類型及特點(diǎn)
| 鎖類型 | 適用場(chǎng)景 | 性能開(kāi)銷 | 核心機(jī)制 |
|---|---|---|---|
| 無(wú)鎖(Unlocked) | 無(wú)線程競(jìng)爭(zhēng) | 極低 | 直接通過(guò) CAS 操作嘗試獲取鎖。 |
| 偏向鎖(Biased Locking) | 單線程重復(fù)訪問(wèn)(無(wú)競(jìng)爭(zhēng)) | 極低 | 對(duì)象頭記錄偏向線程 ID,后續(xù)同一線程無(wú)需競(jìng)爭(zhēng),直接獲取鎖。 |
| 輕量級(jí)鎖(Lightweight Lock) | 低競(jìng)爭(zhēng)(多個(gè)線程交替訪問(wèn)) | 中等 | 通過(guò) CAS 自旋嘗試獲取鎖,避免操作系統(tǒng)級(jí)別的阻塞。 |
| 重量級(jí)鎖(Heavyweight Lock) | 高競(jìng)爭(zhēng)(長(zhǎng)時(shí)間阻塞或高并發(fā)) | 高 | 依賴操作系統(tǒng)互斥量(Mutex),線程被掛起并排隊(duì)等待。 |
3. 鎖升級(jí)的過(guò)程
鎖升級(jí)路徑為:無(wú)鎖 → 偏向鎖 → 輕量級(jí)鎖 → 重量級(jí)鎖,且 不可逆(只能升級(jí),不能降級(jí))。
3.1 無(wú)鎖 → 偏向鎖
- 觸發(fā)條件:第一個(gè)線程訪問(wèn)同步代碼塊時(shí)。
- 過(guò)程:
JVM 通過(guò) CAS 操作將對(duì)象頭的
Mark Word標(biāo)記為偏向鎖。記錄當(dāng)前線程 ID 和偏向時(shí)間戳。
后續(xù)同一線程再次訪問(wèn)時(shí),直接通過(guò)比對(duì)線程 ID 獲取鎖(無(wú)需 CAS 操作)。
3.2 偏向鎖 → 輕量級(jí)鎖
- 觸發(fā)條件:第二個(gè)線程嘗試獲取同一鎖(出現(xiàn)競(jìng)爭(zhēng))。
- 過(guò)程:
偏向鎖失效,JVM 撤銷偏向鎖(可能觸發(fā) STW,Stop-The-World)。
線程通過(guò)自旋(Spin)和 CAS 操作嘗試獲取鎖。
若自旋成功,則升級(jí)為輕量級(jí)鎖;否則繼續(xù)自旋或升級(jí)為重量級(jí)鎖。
3.3 輕量級(jí)鎖 → 重量級(jí)鎖
- 觸發(fā)條件:
- 自旋次數(shù)超過(guò)閾值(默認(rèn) 10 次,可通過(guò)
-XX:PreBlockSpin調(diào)整)。 - 多個(gè)線程同時(shí)競(jìng)爭(zhēng)鎖(如第三個(gè)線程加入競(jìng)爭(zhēng))。
- 自旋次數(shù)超過(guò)閾值(默認(rèn) 10 次,可通過(guò)
- 過(guò)程:
JVM 將鎖升級(jí)為重量級(jí)鎖,對(duì)象頭指向監(jiān)視器(Monitor)。
線程進(jìn)入操作系統(tǒng)內(nèi)核態(tài)的阻塞隊(duì)列,等待調(diào)度器喚醒。
未獲取鎖的線程通過(guò)
ObjectMonitor等待喚醒。
4. 鎖升級(jí)的優(yōu)缺點(diǎn)
4.1 優(yōu)點(diǎn)
- 減少無(wú)競(jìng)爭(zhēng)場(chǎng)景的開(kāi)銷:偏向鎖和輕量級(jí)鎖避免了頻繁的 CAS 和上下文切換。
- 動(dòng)態(tài)適配競(jìng)爭(zhēng)強(qiáng)度:在低競(jìng)爭(zhēng)時(shí)保持高性能,在高競(jìng)爭(zhēng)時(shí)保證線程安全。
4.2 缺點(diǎn)
- 偏向鎖撤銷開(kāi)銷:當(dāng)其他線程競(jìng)爭(zhēng)時(shí),撤銷偏向鎖會(huì)導(dǎo)致 STW,影響性能。
- 重量級(jí)鎖的高開(kāi)銷:在高競(jìng)爭(zhēng)場(chǎng)景下,頻繁的線程阻塞/喚醒會(huì)顯著降低性能。
5. 鎖升級(jí)的優(yōu)化策略
5.1 減少鎖持有時(shí)間
- 優(yōu)化方向:縮短同步代碼塊的執(zhí)行時(shí)間,降低鎖的競(jìng)爭(zhēng)概率。
- 示例:
// 不推薦:鎖持有時(shí)間過(guò)長(zhǎng) synchronized (lock) { // 復(fù)雜計(jì)算或 IO 操作 } // 推薦:僅在關(guān)鍵代碼塊加鎖 int result = doSomeComputation(); // 非同步操作 synchronized (lock) { sharedVariable = result; }
5.2 使用分段鎖(Segment Locking)
- 優(yōu)化方向:將一個(gè)大鎖拆分為多個(gè)小鎖,減少鎖的競(jìng)爭(zhēng)范圍。
- 示例:
ConcurrentHashMap使用分段鎖(JDK 8 后改為 CAS + synchronized)。
5.3 禁用偏向鎖
- 適用場(chǎng)景:頻繁切換線程的場(chǎng)景(如高并發(fā)服務(wù))。
- JVM 參數(shù):
-XX:-UseBiasedLocking # 禁用偏向鎖
5.4 調(diào)整自旋次數(shù)
- 適用場(chǎng)景:輕量級(jí)鎖的自旋可能因 CPU 空閑而浪費(fèi)資源。
- 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ā)偏向鎖撤銷,升級(jí)為輕量級(jí)鎖
}
}
7. 關(guān)鍵 JVM 參數(shù)
| 參數(shù) | 作用 |
|---|---|
-XX:+UseBiasedLocking | 開(kāi)啟/關(guān)閉偏向鎖(默認(rèn)開(kāi)啟,Java 15+ 默認(rèn)關(guān)閉)。 |
-XX:BiasedLockingStartupDelay=0 | 立即啟用偏向鎖(避免延遲)。 |
-XX:PreBlockSpin | 設(shè)置輕量級(jí)鎖自旋次數(shù)(默認(rèn) 10)。 |
-XX:-UseSpinning | 關(guān)閉自旋鎖(強(qiáng)制進(jìn)入重量級(jí)鎖)。 |
8. 總結(jié)
- 鎖升級(jí)是 JVM 自動(dòng)管理的機(jī)制,開(kāi)發(fā)者無(wú)需手動(dòng)干預(yù),但理解其原理有助于優(yōu)化并發(fā)性能。
- 偏向鎖適合單線程場(chǎng)景,輕量級(jí)鎖適合低競(jìng)爭(zhēng)場(chǎng)景,重量級(jí)鎖適合高競(jìng)爭(zhēng)場(chǎng)景。
- 鎖升級(jí)不可逆,一旦升級(jí)到重量級(jí)鎖,后續(xù)操作將始終使用重量級(jí)鎖。
通過(guò)合理設(shè)計(jì)代碼(如減少鎖粒度、避免過(guò)早膨脹到重量級(jí)鎖),可以最大化 Java 的并發(fā)性能。
到此這篇關(guān)于Java鎖升級(jí)機(jī)制的文章就介紹到這了,更多相關(guān)Java鎖升級(jí)機(jī)制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java ssm框架實(shí)現(xiàn)分頁(yè)功能的示例代碼(oracle)
這篇文章主要介紹了java ssm框架實(shí)現(xiàn)分頁(yè)功能的示例代碼(oracle),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-03-03
使用idea連接瀏覽器進(jìn)行訪問(wèn)過(guò)程
配置IDEA WebBrowsers路徑:打開(kāi)IDEA,進(jìn)入File→Setting,搜索Web,設(shè)置瀏覽器絕對(duì)路徑至ProgramFiles或ProgramFiles(x86)(根據(jù)程序位數(shù)),復(fù)制路徑解決訪問(wèn)問(wèn)題2025-09-09
IDEA個(gè)性化設(shè)置注釋模板詳細(xì)講解版
IDEA自帶的注釋模板不是太好用,我本人到網(wǎng)上搜集了很多資料系統(tǒng)的整理了一下制作了一份比較完整的模板來(lái)分享給大家,下面這篇文章主要給大家介紹了IDEA個(gè)性化設(shè)置注釋模板的相關(guān)資料,需要的朋友可以參考下2024-01-01
Spring Validator接口校驗(yàn)與全局異常處理器
這篇文章主要介紹了Spring Validator接口校驗(yàn)與全局異常處理器,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11
Spring MVC的參數(shù)綁定和返回值問(wèn)題
這篇文章主要介紹了Spring MVC的參數(shù)綁定和返回值問(wèn)題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-02-02
mybatis generator 使用方法教程(生成帶注釋的實(shí)體類)
下面小編就為大家?guī)?lái)一篇mybatis generator 使用方法教程(生成帶注釋的實(shí)體類)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-08-08

