Java的鎖機制問題

鎖機制

1.鎖監視器

在 Java 并發編程中,鎖監視器(Monitor) 是對象內部與鎖關聯的同步機制,用于控制多線程對共享資源的訪問。以下是核心要點:


🔒 監視器的核心組成

  1. 獨占區(Ownership)

    • 一次僅允許一個線程持有監視器(即獲得鎖)
    • 通過 synchronized 關鍵字實現
  2. 入口區(Entry Set)

    • 競爭鎖的線程隊列(未獲得鎖的線程在此等待)
  3. 等待區(Wait Set)

    • 調用 wait() 的線程釋放鎖后進入此區域
    • 需通過 notify()/notifyAll() 喚醒

?? 關鍵操作

操作作用觸發條件
synchronized線程嘗試獲取監視器鎖,成功則進入獨占區,失敗則阻塞在入口區進入同步代碼塊/方法時
wait()釋放鎖并進入等待區,線程狀態變為 WAITING必須在持有鎖時調用 (synchronized 內)
notify()隨機喚醒一個等待區的線程(喚醒后需重新競爭鎖)必須在持有鎖時調用
notifyAll()喚醒所有等待區的線程必須在持有鎖時調用

🔄 工作流程示例

public class MonitorDemo {private final Object lock = new Object(); // 鎖監視器關聯到此對象public void doWork() throws InterruptedException {synchronized (lock) { // 1. 線程進入獨占區while (條件不滿足) {lock.wait(); // 2. 釋放鎖并進入等待區}// 3. 執行臨界區代碼lock.notifyAll(); // 4. 喚醒其他等待線程}}
}

?? 重要規則

  1. 鎖綁定對象
    每個 Java 對象(包括 Class 對象)都有一個內置監視器

    synchronized(obj) { ... }  // 使用對象監視器
    synchronized(MyClass.class) { ... }  // 使用類對象的監視器
    
  2. wait/notify 必須持有鎖
    未在 synchronized 塊中調用會拋出 IllegalMonitorStateException

  3. 虛假喚醒(Spurious Wakeup)
    等待線程可能被隨機喚醒(即使未調用 notify),必須用循環檢查條件:

    while (!condition) { obj.wait();
    }
    

💡 監視器 vs 顯式鎖(ReentrantLock)

特性內置監視器ReentrantLock
實現方式JVM 內置JDK 代碼實現 (java.util.concurrent)
鎖獲取隱式獲取/釋放顯式調用 lock()/unlock()
等待條件單一等待隊列支持多個 Condition 隊列
公平性非公平鎖可選公平/非公平模式
中斷響應不支持支持 lockInterruptibly()

📌 總結

  • 監視器 = 鎖 + 等待隊列 + 線程調度機制
  • synchronized 實現鎖的互斥訪問
  • wait()/notify() 實現線程間協作
  • 經典應用場景:生產者-消費者模型、線程間條件等待

?? 注意:優先使用 java.util.concurrent 包中的高級工具(如 ReentrantLockCondition),它們提供更靈活的同步控制,能避免許多內置監視器的限制。

2.鎖升級

在 Java 并發編程中,鎖升級(Lock Escalation) 是 JVM 對 synchronized 鎖的優化機制,它會根據鎖競爭情況動態調整鎖狀態,從低開銷狀態逐步升級到高開銷狀態。這是 Java 性能優化的關鍵特性(自 JDK 1.6 引入)。

🔄 鎖升級的四個階段

無鎖
偏向鎖
輕量級鎖
重量級鎖

1. 無鎖狀態(No Lock)

  • 特征:對象剛創建時,沒有任何線程訪問
  • 開銷:無任何鎖操作成本
  • 對象頭標志001

2. 偏向鎖(Biased Lock)

  • 適用場景單線程重復訪問同步塊
  • 優化原理
    • 在對象頭記錄首個獲得鎖的線程ID
    • 同一線程后續進入同步塊時無需 CAS 操作
  • 對象頭標志101
  • 升級觸發:當其他線程嘗試獲取鎖時

3. 輕量級鎖(Lightweight Lock)

  • 適用場景多線程交替執行(無實際競爭)
  • 實現機制
    1. 在棧幀創建鎖記錄(Lock Record)
    2. 通過 CAS 將對象頭替換為指向鎖記錄的指針
    3. 成功:獲得鎖;失敗:自旋嘗試
  • 對象頭標志00
  • 升級觸發:自旋超過閾值(默認10次)或自旋時出現第三個線程競爭

4. 重量級鎖(Heavyweight Lock)

  • 適用場景高并發競爭
  • 實現機制
    • 通過操作系統 mutex 互斥量實現
    • 未獲鎖線程進入阻塞隊列(涉及內核態切換)
  • 對象頭標志10
  • 特點:開銷最大,但保證公平性

🧪 鎖升級過程示例

public class LockEscalationDemo {private static final Object lock = new Object();private static int counter = 0;public static void main(String[] args) {// 階段1: 偏向鎖 (單線程)synchronized (lock) {counter++;}// 階段2: 輕量級鎖 (多線程交替)new Thread(() -> {for (int i = 0; i < 5; i++) {synchronized (lock) { counter++; }}}).start();// 階段3: 重量級鎖 (高并發競爭)for (int i = 0; i < 10; i++) {new Thread(() -> {synchronized (lock) { counter++; }}).start();}}
}

📊 鎖狀態對比表

特性偏向鎖輕量級鎖重量級鎖
適用場景單線程訪問多線程交替執行高并發競爭
實現方式記錄線程IDCAS自旋操作系統mutex
開銷極低中等
競爭處理升級為輕量級鎖自旋失敗則升級線程阻塞
對象頭存儲線程ID+epoch指向棧中鎖記錄指針指向監視器對象指針
是否阻塞自旋(非阻塞)是(內核阻塞)
公平性可配置

?? 鎖升級關鍵技術細節

  1. Mark Word 結構變化

    // 32位JVM對象頭示例
    | 鎖狀態   | 25bit          | 4bit     | 1bit(偏向) | 2bit(鎖標志) |
    |----------|----------------|----------|------------|--------------|
    | 無鎖     | 哈希碼         | 分代年齡 | 0          | 01           |
    | 偏向鎖   | 線程ID+epoch   | 分代年齡 | 1          | 01           |
    | 輕量級鎖 | 指向鎖記錄指針 |          |            | 00           |
    | 重量級鎖 | 指向監視器指針 |          |            | 10           |
    
  2. 批量重偏向(Bulk Rebias)

    • 當一類對象的偏向鎖被撤銷超過閾值(默認20次),JVM 會認為該類不適合偏向鎖
    • 后續該類的對象會直接進入輕量級鎖狀態
  3. 鎖消除(Lock Elision)

    • JIT 編譯器對不可能存在共享競爭的鎖進行消除

      // 示例:局部StringBuffer的同步會被消除
      public String localMethod() {StringBuffer sb = new StringBuffer(); // 局部變量sb.append("Hello");return sb.toString();
      }
      

?? 重要注意事項

  1. 鎖降級不存在

    • 鎖升級是單向過程(偏向→輕量→重量)
    • 一旦升級為重量級鎖,不會降級(即使競爭消失)
  2. 偏向鎖延遲啟動

    • JVM 啟動后前 4 秒默認禁用偏向鎖(避免初始化時的無效偏向)

      # 關閉偏向鎖(JDK 15+默認)
      -XX:-UseBiasedLocking
      
  3. 自旋優化

    • 輕量級鎖的自旋次數由 JVM 自適應調整(Adaptive Spinning)
    • 基于前一次鎖獲取的成功率動態變化

💡 最佳實踐建議

  1. 低競爭場景

    • 保持默認設置(允許鎖升級)
    • 避免不必要的同步塊
  2. 高競爭場景

    • 考慮使用 ReentrantLock 替代 synchronized
    • 利用 java.util.concurrent 高級并發工具
  3. 性能調優

    # 查看鎖競爭情況
    -XX:+PrintSynchronizationStatistics# 禁用偏向鎖(若確認高競爭)
    -XX:-UseBiasedLocking
    

鎖升級的本質:JVM 在線程安全執行效率之間尋找最佳平衡點,開發者應理解其原理但避免過度干預自動優化。

3.ABA問題

ABA 問題詳解

在并發編程中,ABA 問題是使用 CAS(Compare-And-Swap)操作時可能遇到的一種經典問題。它發生在共享變量的值經歷了 A→B→A 的變化序列后,CAS 操作無法檢測到中間狀態變化的情況。

🔍 ABA 問題發生機制

線程1 共享變量 線程2 讀取值 A 修改值 A→B 修改值 B→A 執行CAS(A→C):成功! 線程1 共享變量 線程2

問題本質

  • CAS 只檢查值是否匹配,不關心值是否被修改過
  • 雖然最終值回到了 A,但中間狀態變化被忽略
  • 可能導致數據一致性問題

?? 經典案例:無鎖棧實現中的 ABA

public class Stack {private AtomicReference<Node> top = new AtomicReference<>();public void push(Node node) {Node oldTop;do {oldTop = top.get();node.next = oldTop;} while (!top.compareAndSet(oldTop, node));}public Node pop() {Node oldTop;Node newTop;do {oldTop = top.get();if (oldTop == null) return null;newTop = oldTop.next;} while (!top.compareAndSet(oldTop, newTop));return oldTop;}
}

ABA 問題發生場景:

  1. 線程1讀取棧頂節點 A
  2. 線程1被掛起
  3. 線程2彈出 A,棧頂變為 B
  4. 線程2彈出 B
  5. 線程2壓入 A(新節點,地址相同)
  6. 線程1恢復執行,CAS 成功將 A 替換為 C
  7. 結果:C.next 指向 B,但 B 已被彈出,造成內存錯誤

🛡? ABA 問題解決方案

1. 版本號機制(推薦)

為每個狀態變化添加版本號戳記:

// Java 內置解決方案
AtomicStampedReference<V> // 帶整數戳記的引用
AtomicMarkableReference<V> // 帶布爾標記的引用

實現原理

匹配
不匹配
版本戳
CAS操作
同時檢查值和版本號
更新值和版本號
操作失敗

使用示例

public class ABASolution {private AtomicStampedReference<Integer> value = new AtomicStampedReference<>(0, 0); // 初始值=0, 版本=0public void update(int expectedValue, int newValue) {int[] stampHolder = new int[1];int oldStamp;int newStamp;do {// 讀取當前值和版本int currentValue = value.get(stampHolder);oldStamp = stampHolder[0];// 驗證值是否被修改過if (currentValue != expectedValue) {break; // 值已被其他線程修改}newStamp = oldStamp + 1; // 更新版本號} while (!value.compareAndSet(expectedValue, newValue, oldStamp, newStamp));}
}

2. 不重復使用內存地址

  • 確保被替換的對象不會被重用
  • 適用于對象池或資源管理場景
  • 實現復雜,不推薦作為通用方案

3. 延遲回收(GC 語言中)

  • 依賴垃圾回收機制防止對象復用
  • 在非 GC 環境(如 C/C++)中不可靠

📊 ABA 問題與其他并發問題對比

問題類型發生場景檢測難度典型解決方案
ABA 問題CAS 操作版本號機制
競態條件多線程無序訪問同步鎖
死鎖多鎖相互等待鎖排序、超時機制
活鎖線程持續重試失敗隨機退避策略

ABA 問題本質:CAS 操作只能檢查值的相等性,無法檢測值的歷史變化。版本號機制通過添加狀態元數據,將值檢查擴展為狀態機檢查,從而解決這一問題。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/85737.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/85737.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/85737.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

老鳳祥的AI智能眼鏡:讓智慧更近生活

在科技進步的潮流中,人工智能技術不斷為我們的生活增添色彩。近日,有關字節跳動旗下的火山引擎與中國珠寶品牌老鳳祥合作開發 AI 智能眼鏡的消息引發了廣泛關注。這款與眾不同的眼鏡因其獨特的功能及技術支持,已經在業內引起了極大反響。 AI眼鏡:老年群體的智能好幫手 根…

Kotlin 中為什么沒有靜態變量和靜態方法—不用static?

Kotlin 的設計核心是&#xff1a; 一切皆對象&#xff1a;消除 static 的「非對象」特性&#xff0c;用 companion&#xff08;對象&#xff09;和頂層函數&#xff08;包級對象&#xff09;替代&#xff0c;讓代碼更統一。避免全局狀態濫用&#xff1a;static 成員是全局可見…

VSCode性能調優:從卡頓到絲滑的終極方案

? 核心價值 "這套配置使某金融核心系統VS Code內存占用從8GB降至1.2GB,加載速度提升15倍" —— 2024某銀行效能優化報告 ?? 性能瓶頸拆解 一、百萬行項目優化方案 ?? 黃金配置參數 // settings.json(核彈級優化) {"files.watcherExclude": {"…

以云織夢,渡數濟世:輝瑞與亞馬遜云科技共譜醫藥新樂章

胖頭陀科技 編輯&#xff1a;沐由 【導讀】“用合規的數據來幫助患者&#xff0c;成為患者回歸健康的一味新藥。”當下&#xff0c;在數字洪流的浪潮中&#xff0c;這味“良藥”正沿著云和AI的脈絡&#xff0c;奔向有需求的千家萬戶…… 如果說到Pfizer&#xff0c;估計十個人…

SpringBoot后端開發知識點總結(持續更新)

目錄 1. 常用易混淆注解解釋1.1 Resource和Autowired注解的區別1.2 PathVariable和RequestParam注解的區別 2. Mybatis-Plus高級特性2.1 強大的通用CRUD接口2.2 代碼生成器 3. IDEA實用快捷鍵4. 前后端聯調關鍵點4.1 代碼示例4.2 聯調要點4.3 調試技巧 1. 常用易混淆注解解釋 …

電腦商城--用戶收貨管理

新增收貨地址 1 新增收貨地址-創建數據表 1.使用use命令先選中store數據庫。 USE store; 2.在store數據庫中創建t_address用戶數據表。 CREATE TABLE t_address (aid INT AUTO_INCREMENT COMMENT 收貨地址id,uid INT COMMENT 歸屬的用戶id,name VARCHAR(20) COMMENT 收貨人姓…

開發者避坑:接入Flux-Kontext API實現文生圖、圖生圖功能

在數字化浪潮背景下&#xff0c;人工智能&#xff08;Artificial Intelligence, AI&#xff09;技術正加速重塑圖像創作領域。智創聚合API平臺近日宣布整合Flux-Kontext系列模型&#xff0c;通過API接口支持圖生圖和文生圖功能&#xff0c;為開發者及創作者提供高效解決方案。此…

.Net Core 獲取與bin目錄相同文件路徑的文件

在 .NET Core 中&#xff0c;您可以使用以下方法來獲取與 bin 目錄相同的文件路徑。通常&#xff0c;bin 目錄是應用程序編譯后生成的輸出目錄&#xff0c;您可以使用 AppContext.BaseDirectory 或 Directory.GetCurrentDirectory() 來獲取該目錄的路徑。 以下是一些常用的方法…

RN(React Native)技術應用中常出現的錯誤及解決辦法

React Native 作為跨平臺開發框架&#xff0c;在實際應用中可能會遇到一些常見的錯誤。以下是React Native 技術應用中常出現的錯誤及解決辦法&#xff1a; 1. 網絡請求失敗&#xff08;Network Request Failed&#xff09; 原因&#xff1a; 請求地址不正確網絡權限未配置i…

Java 21 的虛擬線程與橋接模式:構建高性能并發系統

Java 21 的虛擬線程與橋接模式&#xff1a;構建高性能并發系統 &#x1f31f; 嗨&#xff0c;我是IRpickstars&#xff01; &#x1f30c; 總有一行代碼&#xff0c;能點亮萬千星辰。 &#x1f50d; 在技術的宇宙中&#xff0c;我愿做永不停歇的探索者。 ? 用代碼丈量世界&…

HTML5 火焰字體效果教程

HTML5 火焰字體效果教程 這里寫目錄標題 HTML5 火焰字體效果教程前言項目概述基本原理項目結構詳細實現步驟1. HTML結構2. CSS樣式3. JavaScript實現 代碼詳解1. 初始化設置2. 粒子系統3. 生成粒子4. 動畫循環5. 交互控制 擴展和優化建議總結完整代碼 前言 在這篇教程中&#…

SMOTE-XGBoost實戰:金融風控中欺詐檢測的樣本不平衡解決方案

1. 行業問題背景 &#xff08;1&#xff09;金融欺詐檢測的特殊性 在支付風控領域&#xff0c;樣本不平衡是核心痛點。Visa 2023年度報告顯示&#xff0c;全球信用卡欺詐率約為0.6%&#xff0c;但單筆欺詐交易平均損失高達$500。傳統機器學習模型在此場景下表現堪憂&#xff1…

Instagram下載保存 -下載狗解析工具

在日常瀏覽Instagram時&#xff0c;是否有過這樣的煩惱&#xff1a;看到一個精彩的視頻&#xff0c;想要保存下來&#xff0c;卻不知道如何操作&#xff1f;有時候我們會看到一些特別的旅行視頻、搞笑片段&#xff0c;甚至是喜歡的名人分享的內容&#xff0c;簡直是舍不得錯過。…

flink如何基于Pekko實現RPC調用

摘要 通過閱讀flink源碼&#xff0c;了解flink是如何基于Pekko實現遠程RPC調用的 Pekko實現遠程調用 Flink 的 RPC 框架底層是構建在 Pekko 的 actor 模型之上的&#xff0c;了解Pekko如何使用&#xff0c;對后續源碼的閱讀有幫助。 Apache Pekko&#xff08;原為 Akka 的一…

Kafka節點注冊沖突問題分析與解決

一、核心錯誤分析 ERROR Error while creating ephemeral at /brokers/ids/1, node already exists and owner does not match org.apache.zookeeper.KeeperException$NodeExistsException: KeeperErrorCode NodeExists問題本質&#xff1a;ZooKeeper中已存在ID為1的broker節…

突破PPO訓練效率瓶頸!字節跳動提出T-PPO,推理LLM訓練速度提升2.5倍

突破PPO訓練效率瓶頸&#xff01;字節跳動提出T-PPO&#xff0c;推理LLM訓練速度提升2.5倍 在大語言模型&#xff08;LLM&#xff09;通過長思維鏈&#xff08;CoT&#xff09;展現出強大推理能力的當下&#xff0c;強化學習&#xff08;RL&#xff09;作為關鍵技術卻面臨訓練…

【Python】dictionary

1 字典功能 字典是可變容器模型&#xff0c;且可存儲任意類型對象&#xff1b; 字典的每個鍵值對 <key: value> 用冒號 : 分割&#xff0c;每個對之間用逗號(,)分割&#xff0c;整個字典包括在花括號 {} 中 ,格式如下所示&#xff1a; d {key1 : value1, key2 : value…

【python】If 語句

1 使用if 進行條件判斷 1.1 檢查字符串是否相等 car bmw car BMW # FALSEcar bmw car.upper() BMW # true # 變小寫用方法&#xff1a;lower1.2 檢查字符串是否不相等 my_car yadeaif my_car ! Audi:print("Buy one! Buy one! Buy one!")1.3 比較數字 answe…

Knife4j 使用詳解

一、概述 Knife4j 是一款基于 Swagger 的開源 API 文檔工具&#xff0c;旨在為 Java 開發者提供更美觀、功能更強大的 API 文檔生成、展示和調試體驗。它是 Swagger-Bootstrap-UI 的升級版&#xff0c;通過增強 UI 界面和擴展功能&#xff0c;解決了原生 Swagger UI 界面簡陋、…

Java excel坐標計算

package com.common.base.util.excel;/*** excel 坐標計算*/ public class UtilExcelPosi {/*** deepseek生成 ExcelProperty(index UtilExcelPosi.pA)*/public final static int pA 0;public final static int pB 1;public final static int pC 2;public final static i…