最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java鎖機(jī)制完整學(xué)習(xí)筆記(附詳細(xì)代碼)

 更新時(shí)間:2025年12月23日 10:58:37   作者:井九111  
Java中的鎖機(jī)制是并發(fā)編程的核心工具之一,通過合理使用鎖、、讀寫鎖和自旋鎖,開發(fā)者可以確保多線程環(huán)境下的數(shù)據(jù)一致性和程序正確性,這篇文章主要介紹了Java鎖機(jī)制完整學(xué)習(xí)的相關(guān)資料,需要的朋友可以參考下

一、鎖的起源:為什么需要鎖?

1.1 問題場(chǎng)景

int count = 0;

// 線程A 和 線程B 同時(shí)執(zhí)行
count++;

count++ 在CPU層面是三步操作:

1. 讀取 count 的值到寄存器
2. 寄存器的值 +1
3. 把結(jié)果寫回內(nèi)存

1.2 并發(fā)問題

線程A: 讀取count=0
線程B: 讀取count=0  
線程A: 計(jì)算0+1=1
線程B: 計(jì)算0+1=1
線程A: 寫入count=1
線程B: 寫入count=1

結(jié)果:count=1,而不是期望的2

核心問題:如何讓一組操作不可分割地執(zhí)行?

二、volatile 能解決嗎?

2.1 答案:不能

volatile int count = 0;

// 依然有問題
count++;

2.2 volatile 解決的是

特性說明
可見性一個(gè)線程修改后,其他線程立即看到
有序性禁止指令重排序

不解決原子性 —— "讀-改-寫"之間,別的線程還是可以插進(jìn)來。

三、CAS:硬件級(jí)別的原子操作

3.1 CPU 提供的能力

CPU 提供特殊指令(x86 的 CMPXCHG):

比較內(nèi)存值是否等于預(yù)期值
如果相等,就把新值寫入
整個(gè)過程不可被打斷

這就是 Compare-And-Swap。

3.2 技術(shù)棧關(guān)系

CPU 提供:CAS指令(硬件能力,一直都有)
    ↓
JVM 實(shí)現(xiàn):Unsafe類(Java訪問底層能力的橋梁)
    ↓
JDK 封裝:LockSupport、Atomic類等

四、Java 鎖的演進(jìn)歷程

4.1 演進(jìn)時(shí)間線

JDK 1.0:synchronized(重量級(jí)鎖)
JDK 1.5:ReentrantLock、CAS、AQS
JDK 1.6:鎖升級(jí)(偏向鎖、輕量級(jí)鎖)
JDK 1.8:StampedLock(樂觀讀)
JDK 15: 廢棄偏向鎖
JDK 21: 虛擬線程,synchronized 有 Pinning 問題
JDK 24+:修復(fù) synchronized 支持虛擬線程

五、JDK 1.0:synchronized 重量級(jí)鎖

5.1 實(shí)現(xiàn)方式

synchronized (obj) {
    count++;
}

早期實(shí)現(xiàn):

  • 直接調(diào)用操作系統(tǒng)的互斥量(Mutex)
  • 需要從用戶態(tài)切換到內(nèi)核態(tài)
  • 讓搶不到鎖的線程進(jìn)入阻塞狀態(tài)

5.2 內(nèi)核態(tài)切換的開銷

為什么需要切換?

用戶態(tài)(User Mode):Java程序運(yùn)行的地方,權(quán)限受限
內(nèi)核態(tài)(Kernel Mode):操作系統(tǒng)運(yùn)行的地方,權(quán)限最高

阻塞線程、喚醒線程等操作需要操作系統(tǒng)介入,必須切換。

切換時(shí)發(fā)生了什么?

1. 保存現(xiàn)場(chǎng)
   - 當(dāng)前所有CPU寄存器的值
   - 程序計(jì)數(shù)器(執(zhí)行到哪了)
   - 棧指針
   → 全部存到內(nèi)存

2. 切換棧
   - 從用戶棧切到內(nèi)核棧

3. 執(zhí)行內(nèi)核代碼
   - 內(nèi)核的代碼和數(shù)據(jù)被加載到CPU緩存
   - 程序的代碼和數(shù)據(jù)可能被擠出緩存

4. 返回用戶態(tài)
   - 恢復(fù)之前保存的所有寄存器
   - 切回用戶棧
   - 重新加載程序的代碼和數(shù)據(jù)到緩存

開銷量化

CPU緩存訪問:約 1-10 納秒
內(nèi)存訪問:約 100 納秒

一次 count++:        約 幾個(gè) CPU周期
一次內(nèi)核態(tài)切換:      約 幾千~幾萬個(gè) CPU周期

問題:鎖本身的開銷 >> 臨界區(qū)代碼的開銷

六、JDK 1.5:ReentrantLock 與 AQS

6.1 改進(jìn)思路

先用 CAS 自旋幾次(用戶態(tài),不切換)
    ↓
還拿不到?再調(diào)用 park 阻塞(才進(jìn)內(nèi)核態(tài))

能不阻塞就不阻塞。

6.2 對(duì)比

早期 synchronized:
拿不到 → 立即阻塞 → 進(jìn)內(nèi)核態(tài)

ReentrantLock:
拿不到 → 先自旋 → 還不行再阻塞

特性Thread.sleep()Object.wait()LockSupport.park()
鎖釋放不釋放任何鎖釋放 synchronized 監(jiān)視器鎖不直接操作鎖
喚醒方式超時(shí)自動(dòng)喚醒notify()/notifyAll()unpark(Thread)
喚醒目標(biāo)只能喚醒自己隨機(jī)或全部精確喚醒指定線程
使用位置任何地方必須在 synchronized 塊內(nèi)任何地方

6.4 AQS 核心結(jié)構(gòu)

AQS = state 狀態(tài)變量 + CLH 雙向隊(duì)列

CLH:Craig, Landin, and Hagersten Queue,三個(gè)發(fā)明者的名字。

七、JDK 1.6:synchronized 鎖升級(jí)

7.1 核心思想

根據(jù)競(jìng)爭(zhēng)激烈程度,逐步升級(jí):

無鎖 → 偏向鎖 → 輕量級(jí)鎖 → 重量級(jí)鎖

7.2 對(duì)象頭 Mark Word

每個(gè) Java 對(duì)象在內(nèi)存中都有對(duì)象頭,其中 Mark Word(64位)存儲(chǔ)鎖信息:

鎖標(biāo)志位(最后2位):
  01 → 無鎖 或 偏向鎖(看倒數(shù)第3位區(qū)分)
  00 → 輕量級(jí)鎖
  10 → 重量級(jí)鎖

詳細(xì)結(jié)構(gòu):
┌─────────────────────────────────────────────────┐
│ 無鎖:   │ hashCode │ 分代年齡 │ 0 │ 01 │       │
│ 偏向鎖: │ 線程ID │ epoch │ 分代年齡 │ 1 │ 01 │  │
│ 輕量級(jí): │ 指向棧中鎖記錄的指針           │ 00 │  │
│ 重量級(jí): │ 指向Monitor對(duì)象的指針          │ 10 │  │
└─────────────────────────────────────────────────┘

7.3 偏向鎖

場(chǎng)景:只有一個(gè)線程反復(fù)訪問

// 線程A 第一次進(jìn)入
synchronized (lock) {
    count++;
}
// 對(duì)象頭寫入線程A的ID

// 線程A 再次進(jìn)入
synchronized (lock) {
    count++;
}
// 檢查線程ID == A?直接進(jìn)入,零開銷

特點(diǎn):

  • 加鎖:比較線程ID,匹配就進(jìn)入
  • 解鎖:什么都不做,對(duì)象頭保持不變

7.4 輕量級(jí)鎖

場(chǎng)景:多個(gè)線程交替訪問,無激烈競(jìng)爭(zhēng)

觸發(fā)條件: 第二個(gè)線程來訪問時(shí)

// 線程A 進(jìn)入
synchronized (lock) {
    count++;
}
// CAS 修改對(duì)象頭,指向A的鎖記錄

// 線程A 離開
// CAS 恢復(fù)對(duì)象頭,還原原來的Mark Word

特點(diǎn):

  • 每次都要執(zhí)行 CAS 加鎖和解鎖
  • 開銷比偏向鎖大,但仍在用戶態(tài)

7.5 重量級(jí)鎖

場(chǎng)景:CAS 自旋多次仍失敗,競(jìng)爭(zhēng)激烈

Monitor 結(jié)構(gòu):

┌─────────────────────────┐
│  Owner: 當(dāng)前持有鎖的線程   │
│  EntryList: 阻塞等待的線程 │  ← 搶鎖失敗的線程排隊(duì)
│  WaitSet: 調(diào)用wait()的線程 │
└─────────────────────────┘

7.6 鎖升級(jí)流程圖

lock 對(duì)象剛創(chuàng)建
    │
    ▼
  無鎖(0 01)
    │
    │ 線程A第一次訪問
    ▼
偏向鎖(1 01)── 對(duì)象頭記錄線程A的ID
    │
    │ 線程B來訪問
    ▼
輕量級(jí)鎖(00)── CAS自旋競(jìng)爭(zhēng)
    │
    │ 自旋失敗,競(jìng)爭(zhēng)激烈
    ▼
重量級(jí)鎖(10)── 阻塞等待,進(jìn)內(nèi)核態(tài)

7.7 三種鎖的開銷對(duì)比

偏向鎖:  比較ID → 進(jìn)入          (一次比較)
輕量級(jí)鎖:CAS修改指針 → 進(jìn)入      (一次原子操作)
重量級(jí)鎖:系統(tǒng)調(diào)用 → 可能阻塞     (內(nèi)核態(tài)切換)

7.8 鎖升級(jí)的特點(diǎn)

1. 單向升級(jí),不會(huì)降級(jí)
2. 一旦出現(xiàn)過競(jìng)爭(zhēng),JVM認(rèn)為后續(xù)還可能有競(jìng)爭(zhēng)

八、JDK 15:廢棄偏向鎖

8.1 原因

現(xiàn)代應(yīng)用并發(fā)程度高,單線程訪問場(chǎng)景少
偏向鎖撤銷的開銷 > 它帶來的收益

8.2 變化

鎖升級(jí)變成:

無鎖 → 輕量級(jí)鎖 → 重量級(jí)鎖

跳過了偏向鎖

九、讀寫鎖

9.1 Java 讀寫鎖(ReentrantReadWriteLock)

存儲(chǔ)位置:AQS 的 state 變量(32位 int)

┌─────────────────┬─────────────────┐
│  高16位:讀鎖數(shù)量  │  低16位:寫鎖數(shù)量  │
└─────────────────┴─────────────────┘

9.2 Java 與數(shù)據(jù)庫鎖對(duì)應(yīng)關(guān)系

Java數(shù)據(jù)庫
讀鎖(Read Lock)共享鎖(S鎖)
寫鎖(Write Lock)排他鎖(X鎖)

9.3 兼容矩陣

        讀鎖      寫鎖
讀鎖    兼容 ?    沖突 ?
寫鎖    沖突 ?    沖突 ?

核心:讀讀不沖突,其他都沖突

十、數(shù)據(jù)庫鎖

10.1 意向鎖(Intention Lock)

解決的問題:

事務(wù)A:鎖住某一行(行級(jí)X鎖)
事務(wù)B:想鎖整張表(表級(jí)X鎖)
    ↓
事務(wù)B 需要確保表里沒有任何行被鎖住
    ↓
沒有意向鎖:遍歷100萬行檢查
有意向鎖:檢查表級(jí)標(biāo)記,直接判斷

工作流程:

事務(wù)A:
  1. 先給表加 IX鎖(意向排他鎖)
  2. 再給行加 X鎖

事務(wù)B 想加表鎖:
  1. 檢查表級(jí)別有沒有意向鎖
  2. 發(fā)現(xiàn)有 IX鎖 → 直接等待

意向鎖兼容矩陣:

        IS    IX    S     X
IS      ?     ?     ?     ?
IX      ?     ?     ?     ?
S       ?     ?     ?     ?
X       ?     ?     ?     ?

10.2 粒度鎖

解決的問題:幻讀

-- 表里有 id = 1, 5, 10 三行

-- 事務(wù)A
SELECT * FROM users WHERE id > 3 AND id < 8;  -- 結(jié)果:id = 5

-- 事務(wù)B 插入新行
INSERT INTO users (id) VALUES (6);
COMMIT;

-- 事務(wù)A 再查一次
SELECT * FROM users WHERE id > 3 AND id < 8;  -- 結(jié)果:id = 5, 6  ← 幻讀

三種鎖:

鎖類型說明作用
記錄鎖(Record Lock)鎖具體的行防止修改/刪除
間隙鎖(Gap Lock)鎖行之間的空隙防止插入
臨鍵鎖(Next-Key Lock)記錄鎖 + 間隙鎖防止幻讀

間隙示例:

表里有 id = 1, 5, 10

間隙:(-∞, 1)  (1, 5)  (5, 10)  (10, +∞)

查詢 id > 3 AND id < 8 時(shí):
鎖住間隙 (1, 5) 和 (5, 10)
    ↓
插入 id = 6 被阻塞

十一、StampedLock(JDK 1.8)

11.1 解決的問題

ReentrantReadWriteLock 問題:
1. 讀鎖和寫鎖互斥,大量讀時(shí)寫線程饑餓
2. 即使只是讀,也要加鎖

11.2 樂觀讀

StampedLock lock = new StampedLock();

long stamp = lock.tryOptimisticRead();  // 獲取版本號(hào),不加鎖
int x = this.x;  // 讀數(shù)據(jù)
int y = this.y;

if (!lock.validate(stamp)) {  // 驗(yàn)證版本號(hào)
    // 版本變了,升級(jí)為悲觀讀鎖
    stamp = lock.readLock();
    try {
        x = this.x;
        y = this.y;
    } finally {
        lock.unlockRead(stamp);
    }
}

11.3 三種模式

模式方法說明
樂觀讀tryOptimisticRead()不加鎖,性能最好
悲觀讀readLock()和讀寫鎖一樣
寫鎖writeLock()排他

11.4 對(duì)比

ReentrantReadWriteLockStampedLock
樂觀讀??
可重入??
支持Condition??

十二、公平鎖 vs 非公平鎖

類型說明性能
公平鎖嚴(yán)格按隊(duì)列順序,不允許插隊(duì)較低
非公平鎖允許插隊(duì),誰搶到誰用較高

ReentrantLock 默認(rèn)是非公平的。

十三、可重入鎖

概念: 同一線程可以多次獲取同一把鎖

實(shí)現(xiàn)原理: 判斷鎖的持有者是否是當(dāng)前線程

synchronized (lock) {     // 第一次獲取
    synchronized (lock) { // 同一線程再次獲取,允許
        // ...
    }
}

作用: 避免同一線程自己把自己鎖死

十四、JDK 21+:虛擬線程與 Pinning 問題

14.1 虛擬線程原理

平臺(tái)線程(OS線程):重量級(jí),數(shù)量有限
虛擬線程:輕量級(jí),可以創(chuàng)建百萬個(gè)

┌─────────────────────────────────────┐
│  平臺(tái)線程(載體線程)                  │
│  ┌─────┐ ┌─────┐ ┌─────┐            │
│  │虛擬1│ │虛擬2│ │虛擬3│  輪流執(zhí)行    │
│  └─────┘ └─────┘ └─────┘            │
└─────────────────────────────────────┘

正常情況:

虛擬線程1 執(zhí)行中,遇到阻塞
    ↓
JVM 把虛擬線程1 從平臺(tái)線程上"卸載"
    ↓
平臺(tái)線程去執(zhí)行虛擬線程2
    ↓
虛擬線程1 的阻塞完成后,再"掛載"回來

14.2 Pinning 問題

synchronized (lock) {
    httpClient.send(request);  // 網(wǎng)絡(luò)IO阻塞
}

問題流程:

虛擬線程進(jìn)入 synchronized 塊
    ↓
Monitor(監(jiān)視器)記錄在平臺(tái)線程的棧幀上
    ↓
遇到 IO 阻塞,想卸載虛擬線程
    ↓
但 Monitor 信息和平臺(tái)線程綁定,無法卸載
    ↓
虛擬線程"釘住"了平臺(tái)線程
    ↓
平臺(tái)線程只能傻等

14.3 為什么 ReentrantLock 沒這個(gè)問題?

ReentrantLock 用 Java 代碼實(shí)現(xiàn)(AQS)
    ↓
鎖狀態(tài)存在堆內(nèi)存的 state 變量中
    ↓
不依賴平臺(tái)線程的棧
    ↓
虛擬線程可以正常卸載

14.4 JDK 24 修復(fù)(JEP 491)

重新實(shí)現(xiàn) synchronized 的底層機(jī)制
    ↓
Monitor 信息從棧上移到堆上
    ↓
不再和平臺(tái)線程綁定
    ↓
虛擬線程可以正常卸載

對(duì),你說得很準(zhǔn)確。我更新一下總結(jié):

十五、總結(jié):鎖的本質(zhì)

兩種實(shí)現(xiàn)路徑

鎖類型爭(zhēng)搶目標(biāo)存儲(chǔ)位置
synchronized對(duì)象頭 Mark Word對(duì)象內(nèi)存布局
ReentrantLock / StampedLockstate 狀態(tài)變量AQS 堆內(nèi)存

核心機(jī)制

synchronized:
    CAS 修改對(duì)象頭 → 成功則拿到鎖

JUC 鎖:
    CAS 修改 state 變量 → 成功則拿到鎖

演進(jìn)思路

能不阻塞就不阻塞
能在用戶態(tài)解決就不進(jìn)內(nèi)核態(tài)
根據(jù)競(jìng)爭(zhēng)程度選擇合適的鎖

總結(jié)

到此這篇關(guān)于Java鎖機(jī)制的文章就介紹到這了,更多相關(guān)Java鎖機(jī)制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java中Scanner.next()和Scanner.nextLine的區(qū)別圖文詳解

    java中Scanner.next()和Scanner.nextLine的區(qū)別圖文詳解

    使用java語言編程,最常用的輸入就是使用Scanner了,它的構(gòu)造很簡(jiǎn)單,這篇文章主要給大家介紹了關(guān)于java中Scanner.next()和Scanner.nextLine區(qū)別的相關(guān)資料,需要的朋友可以參考下
    2024-02-02
  • 詳解如何給Sprintboot應(yīng)用添加插件機(jī)制

    詳解如何給Sprintboot應(yīng)用添加插件機(jī)制

    這篇文章主要為大家介紹了如何給 Sprintboot 應(yīng)用添加插件機(jī)制,文中有詳細(xì)的解決方案及示例代碼,具有一定的參考價(jià)值,需要的朋友可以參考下
    2023-08-08
  • Java中對(duì)null進(jìn)行強(qiáng)制類型轉(zhuǎn)換的方法

    Java中對(duì)null進(jìn)行強(qiáng)制類型轉(zhuǎn)換的方法

    小編對(duì)null進(jìn)行強(qiáng)轉(zhuǎn)會(huì)不會(huì)拋錯(cuò),非常的好奇,下面小編通過實(shí)例代碼給大家介紹Java中對(duì)null進(jìn)行強(qiáng)制類型轉(zhuǎn)換的方法,感興趣的朋友參考下吧
    2018-09-09
  • 解決spring集成redisson踩過的坑

    解決spring集成redisson踩過的坑

    這篇文章主要介紹了spring集成redisson踩過的坑,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • Springboot如何使用mybatis實(shí)現(xiàn)攔截SQL分頁

    Springboot如何使用mybatis實(shí)現(xiàn)攔截SQL分頁

    這篇文章主要介紹了Springboot使用mybatis實(shí)現(xiàn)攔截SQL分頁,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-06-06
  • springboot整合mqtt的詳細(xì)圖文教程

    springboot整合mqtt的詳細(xì)圖文教程

    MQTT是一種基于發(fā)布/訂閱(publish/subscribe)模式的“輕量級(jí)”通訊協(xié)議,該協(xié)議構(gòu)建于TCP/IP協(xié)議上,由IBM在1999年發(fā)布,下面這篇文章主要給大家介紹了關(guān)于springboot整合mqtt的詳細(xì)圖文教程,需要的朋友可以參考下
    2023-02-02
  • Stream流中的Map與flatMap的區(qū)別及說明

    Stream流中的Map與flatMap的區(qū)別及說明

    Map轉(zhuǎn)換單個(gè)元素為新值,保持流長(zhǎng)度;flatMap將元素轉(zhuǎn)換為流再合并,實(shí)現(xiàn)扁平化處理,兩者區(qū)別在于返回類型(對(duì)象vs流)及是否改變?cè)財(cái)?shù)量,適用于不同數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)換場(chǎng)景
    2025-09-09
  • SpringMVC---配置與使用的示例

    SpringMVC---配置與使用的示例

    這篇文章主要介紹了SpringMVC---配置與使用的示例,幫助大家更好的理解和學(xué)習(xí)spring框架,感興趣的朋友可以了解下
    2020-10-10
  • 淺談Maven安裝及環(huán)境配置出錯(cuò)的解決辦法

    淺談Maven安裝及環(huán)境配置出錯(cuò)的解決辦法

    這篇文章主要介紹了淺談Maven安裝及環(huán)境配置出錯(cuò)的解決辦法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • Java 的 FileFilter文件過濾與readline讀行操作實(shí)例代碼

    Java 的 FileFilter文件過濾與readline讀行操作實(shí)例代碼

    這篇文章介紹了Java 的 FileFilter文件過濾與readline讀行操作實(shí)例代碼,有需要的朋友可以參考一下
    2013-09-09

最新評(píng)論

新巴尔虎右旗| 闽清县| 峨山| 绍兴县| 周口市| 资兴市| 盐津县| 仙桃市| 新巴尔虎左旗| 彩票| 栖霞市| 通河县| 瑞金市| 崇左市| 久治县| 三都| 徐汇区| 敦化市| 盐津县| 通海县| 克什克腾旗| 财经| 庄河市| 同仁县| 璧山县| 昌宁县| 德清县| 穆棱市| 普兰店市| 武川县| 兴隆县| 浮梁县| 盖州市| 台中市| 凤山市| 哈巴河县| 高要市| 云安县| 义马市| 新余市| 康定县|