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

java?synchronized關(guān)鍵字用法和底層原理舉例詳解

 更新時間:2026年01月30日 08:19:44   作者:看透也說透kevin  
synchronized關(guān)鍵字是java語言進行多線程編程,非常常用的關(guān)鍵,這篇文章主要介紹了java?synchronized關(guān)鍵字用法和底層原理的相關(guān)資料,文中通過代碼介紹的非常詳細,需要的朋友可以參考下

一、synchronized的用法

synchronized 關(guān)鍵字用于實現(xiàn)線程同步,確保多個線程在訪問共享資源時不會發(fā)生數(shù)據(jù)競爭和不一致的問題。它主要有三種使用方式:

1. 同步實例方法

public synchronized void method() {
    // 同步代碼
}
  • 鎖對象是當(dāng)前實例(this)。
  • 同一個實例的多個線程調(diào)用此方法時會互斥。

2. 同步靜態(tài)方法

public static synchronized void staticMethod() {
    // 同步代碼
}
  • 鎖對象是當(dāng)前類的 Class 對象(如 MyClass.class)。
  • 所有實例的線程調(diào)用此方法時都會互斥。

3. 同步代碼塊

synchronized (lockObject) {
    // 同步代碼
}
  • 可以靈活指定鎖對象(可以是任意對象)。
  • 縮小了同步范圍,提高性能。

4. 深入:wait(),notify(),notifyAll()

這三個方法是定義在 Object 類中的本地方法,必須在一個同步代碼塊或同步方法內(nèi)(即已經(jīng)持有該對象的監(jiān)視器鎖)調(diào)用,否則會拋出 IllegalMonitorStateException 異常。它們與 synchronized 配合使用,實現(xiàn)線程間的協(xié)調(diào)(等待/通知機制)。

  • wait(): 使當(dāng)前線程釋放其持有的對象鎖,并進入等待狀態(tài)(WAITING),直到其他線程調(diào)用該對象的 notify()notifyAll() 方法,或被中斷。調(diào)用后,線程會釋放鎖
  • notify(): 隨機喚醒一個正在等待該對象鎖的線程。被喚醒的線程會從等待池移動到阻塞隊列(EntryList)中,等待鎖釋放后重新競爭鎖。
  • notifyAll(): 喚醒所有正在等待該對象鎖的線程。這些線程都會被移動到阻塞隊列中,共同競爭鎖。

典型的生產(chǎn)者-消費者模式示例:

public class WaitNotifyExample {
    private final Object lock = new Object();
    private boolean conditionMet = false;

    public void consumer() throws InterruptedException {
        synchronized (lock) {
            // 使用while循環(huán)防止"虛假喚醒"
            while (!conditionMet) {
                lock.wait(); // 釋放lock鎖,當(dāng)前線程進入等待
            }
            // 條件滿足,執(zhí)行消費任務(wù)
            System.out.println("Consuming...");
            conditionMet = false;
            lock.notifyAll(); // 消費完成,通知生產(chǎn)者
        }
    }

    public void producer() throws InterruptedException {
        synchronized (lock) {
            while (conditionMet) {
                lock.wait(); // 如果條件已滿足,則等待消費者消費
            }
            // 生產(chǎn)任務(wù)
            System.out.println("Producing...");
            conditionMet = true;
            lock.notifyAll(); // 生產(chǎn)完成,通知消費者
        }
    }
}

關(guān)鍵點:

  1. 總是需要在循環(huán)中調(diào)用 wait() 以防止虛假喚醒(Spurious Wakeup),即線程可能在沒有被通知、中斷或超時的情況下醒來。
  2. wait() 會釋放鎖,而 sleep() 不會。
  3. 通常更推薦使用 notifyAll(),因為 notify() 是隨機喚醒,可能導(dǎo)致某些線程永遠無法被喚醒(線程饑餓)。

二、底層原理

synchronized 的底層實現(xiàn)依賴于 JVM 內(nèi)部的 監(jiān)視器鎖(Monitor) 機制,主要涉及以下概念:

1. 對象頭與 Mark Word

  • 在 HotSpot 虛擬機中,每個對象都有一個對象頭,其中包含 Mark Word(標(biāo)記字段)。
  • Mark Word 的長度為 32位 或 64位,用于存儲對象的哈希碼、分代年齡、鎖狀態(tài)標(biāo)志位等信息。鎖的狀態(tài)就記錄在這里。

2. 鎖升級的詳細過程

為了平衡性能與安全性,JVM 對 synchronized 進行了優(yōu)化,引入了鎖升級機制。這個過程是單向的:無鎖 → 偏向鎖 → 輕量級鎖 → 重量級鎖。

  1. 無鎖狀態(tài) (01)

    • 初始狀態(tài),對象剛被創(chuàng)建,尚未有任何線程競爭。
  2. 偏向鎖 (Biased Locking) (01)

    • 目的:消除在無競爭情況下的同步開銷。假設(shè)在大多數(shù)情況下,鎖不僅不存在多線程競爭,而且總是由同一線程多次獲得。
    • 流程
      • 加鎖:當(dāng)一個線程第一次訪問同步塊時,它會通過 CAS 操作將自己的 線程ID 寫入對象頭的 Mark Word。如果成功,則進入偏向模式,鎖標(biāo)志位變?yōu)?01,且偏向模式標(biāo)志為 1。
      • 執(zhí)行:此后,只要這個線程再次進入同步塊,只需檢查 Mark Word 中的線程ID是否是自己。如果是,則無需任何同步操作(如CAS、系統(tǒng)互斥)直接執(zhí)行,極大提升性能。
      • 撤銷:一旦出現(xiàn)另一個線程來嘗試競爭鎖,偏向鎖就會撤銷(Revoke Bias)。撤銷過程需要等待全局安全點(STW),然后檢查原持有偏向鎖的線程是否仍活躍。如果活躍,則鎖升級為輕量級鎖;如果已不活躍,則可將對象置為無鎖狀態(tài),重新偏向新的線程。
  3. 輕量級鎖 (Lightweight Locking) (00)

    • 目的:在有多線程競爭,但競爭時間錯開(即近乎交替執(zhí)行)的場景下,避免線程直接進入阻塞,減少用戶態(tài)到內(nèi)核態(tài)的切換開銷。
    • 加鎖流程
      • 線程在執(zhí)行同步塊之前,JVM 會先在當(dāng)前線程的棧幀中創(chuàng)建一個名為鎖記錄(Lock Record) 的空間。
      • 將對象頭的 Mark Word 復(fù)制到線程的鎖記錄中(稱為 Displaced Mark Word)。
      • 線程嘗試通過 CAS 操作將對象頭的 Mark Word 替換為指向其鎖記錄的指針。
      • 如果成功,當(dāng)前線程獲得鎖,鎖標(biāo)志位變?yōu)?00
      • 如果失敗,表示至少有一條線程與當(dāng)前線程競爭,當(dāng)前線程會嘗試自旋(循環(huán)重試CAS操作)來獲取鎖。
    • 解鎖流程
      • 使用 CAS 操作將 Displaced Mark Word 替換回對象頭。
      • 如果成功,表示沒有競爭發(fā)生。
      • 如果失敗,表示當(dāng)前鎖存在競爭,鎖會膨脹為重量級鎖,并在解鎖時喚醒等待的線程。
    • 自旋優(yōu)化:線程不會立即阻塞,而是循環(huán)嘗試獲取鎖。避免了線程上下文切換的開銷,但會消耗CPU。如果自旋過度(如自旋次數(shù)超過閾值,或競爭的線程數(shù)過多),鎖會直接升級為重量級鎖。
  4. 重量級鎖 (Heavyweight Locking) (10)

    • 目的:處理高并發(fā)、激烈競爭的場景。
    • 流程:當(dāng)輕量級鎖競爭失敗后,會膨脹為重量級鎖。此時,Mark Word 中存儲的是指向一個互斥量(Mutex) 的指針,這個互斥量由操作系統(tǒng)內(nèi)核提供。
    • 所有等待鎖的線程都會進入阻塞狀態(tài)(BLOCKED),不再消耗CPU。鎖的獲取和釋放需要操作系統(tǒng)進行線程的掛起喚醒,這個過程涉及到復(fù)雜的用戶態(tài)到內(nèi)核態(tài)的切換,開銷最大。

鎖升級狀態(tài)總結(jié)表:

鎖狀態(tài)存儲內(nèi)容標(biāo)志位適用場景
無鎖對象的哈希碼、分代年齡等01無競爭
偏向鎖持有偏向鎖的線程ID、Epoch、分代年齡、偏向模式標(biāo)志 101只有一個線程訪問同步塊
輕量級鎖指向棧中鎖記錄的指針00多個線程交替訪問,低并發(fā)競爭
重量級鎖指向互斥量(Mutex)的指針10高并發(fā),激烈競爭

3. 底層指令

  • 同步代碼塊通過 monitorentermonitorexit 指令實現(xiàn):
    • 進入同步塊時執(zhí)行 monitorenter,退出時執(zhí)行 monitorexit(包括正常退出和異常退出)。
  • 同步方法通過方法表中的 ACC_SYNCHRONIZED 標(biāo)志標(biāo)識,JVM通過此標(biāo)志判斷是否需要同步。

三、注意事項

  1. 可重入性synchronized 是可重入鎖,同一線程可多次獲取同一把鎖。
  2. 鎖對象不能為 null:同步代碼塊的鎖對象不能是 null,否則會拋出 NullPointerException。
  3. 性能考慮
    • 盡量減小同步范圍(使用代碼塊而非整個方法)。
    • 避免鎖競爭激烈,否則會升級為重量級鎖,降低性能。
  4. 與 Lock 的區(qū)別
    • synchronized 是關(guān)鍵字,JVM 級別實現(xiàn);Lock 是 API 級別實現(xiàn)。
    • Lock 更靈活(可中斷、超時、公平鎖等),但需手動釋放鎖。
  5. wait/notify 使用注意:
    • 必須在同步塊內(nèi)調(diào)用。
    • 使用 while 循環(huán)檢查條件,防止虛假喚醒。
    • 明確鎖對象,調(diào)用 obj.wait() 時必須持有 obj 的鎖。

四、示例代碼

public class SynchronizedExample {
    private final Object lock = new Object();
    private int count = 0;

    // 同步實例方法
    public synchronized void increment() {
        count++;
    }

    // 同步靜態(tài)方法
    public static synchronized void staticMethod() {
        // ...
    }

    // 同步代碼塊
    public void doSomething() {
        synchronized (lock) {
            // 同步代碼
        }
    }
}

總結(jié)

synchronized 是 Java 中最基本的線程同步機制,通過 JVM 的 Monitor 和精細的鎖升級策略(偏向鎖->輕量級鎖->重量級鎖)實現(xiàn)了在高性能無競爭場景和高強度競爭場景下的自適應(yīng)同步。其內(nèi)置的 wait/notify 機制為線程間協(xié)調(diào)提供了基礎(chǔ)。使用時需注意鎖的范圍、競爭情況以及 wait/notify 的正確用法,以避免性能問題和邏輯錯誤。對于更復(fù)雜的場景,可以考慮 java.util.concurrent 包中的高級并發(fā)工具。

到此這篇關(guān)于java synchronized關(guān)鍵字用法和底層原理舉例詳解的文章就介紹到這了,更多相關(guān)java synchronized關(guān)鍵字用法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 詳解Java Bellman-Ford算法原理及實現(xiàn)

    詳解Java Bellman-Ford算法原理及實現(xiàn)

    Bellman-Ford算法與Dijkstra算法類似,都是以松弛操作作為基礎(chǔ),Bellman-Ford算法是對所有邊都進行松弛操作,本文將詳解Bellman-Ford算法原理及實現(xiàn),感興趣的可以了解一下
    2022-07-07
  • java向數(shù)據(jù)庫插入數(shù)據(jù)顯示亂碼的幾種問題解決

    java向數(shù)據(jù)庫插入數(shù)據(jù)顯示亂碼的幾種問題解決

    這篇文章主要給大家介紹了關(guān)于java向數(shù)據(jù)庫插入數(shù)據(jù)顯示亂碼問題的解決方案,文章分別羅列了前臺亂碼的問題、前臺先后臺插入數(shù)據(jù)后臺接收到的數(shù)據(jù)是亂碼以及后臺向數(shù)據(jù)庫插入數(shù)據(jù)是亂碼等幾種情況,需要的朋友可以參考下
    2021-11-11
  • Intellij IDEA調(diào)試技巧的深入講解

    Intellij IDEA調(diào)試技巧的深入講解

    這篇文章主要給大家介紹了關(guān)于Intellij IDEA調(diào)試技巧的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-08-08
  • Java詳細講解包的作用以及修飾符的介紹

    Java詳細講解包的作用以及修飾符的介紹

    本文主要講述的是包的使用和注意事項和四種訪問修飾符public,protected,默認的,private的訪問范圍及實例,感興趣的朋友一起來看看
    2022-05-05
  • Intellij IDEA如何去掉@Autowired 注入警告的方法

    Intellij IDEA如何去掉@Autowired 注入警告的方法

    這篇文章主要介紹了Intellij IDEA如何去掉@Autowired 注入警告的方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-04-04
  • Java多線程、進度條實現(xiàn)賽馬實驗的示例代碼

    Java多線程、進度條實現(xiàn)賽馬實驗的示例代碼

    這篇文章主要介紹了Java多線程、進度條實現(xiàn)賽馬實驗的示例代碼,代碼簡單易懂,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-11-11
  • Java中的靜態(tài)代碼塊使用解讀

    Java中的靜態(tài)代碼塊使用解讀

    本文將深入探討靜態(tài)代碼塊的工作原理、使用場景以及一些最佳實踐,幫助你更好地理解和應(yīng)用這一特性
    2025-02-02
  • IntelliJ IDEA 2020.3 重大特性(新功能一覽)

    IntelliJ IDEA 2020.3 重大特性(新功能一覽)

    這篇文章主要介紹了IntelliJ IDEA 2020.3 重大特性(新功能一覽),本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-12-12
  • 【java 多線程】守護線程與非守護線程的詳解

    【java 多線程】守護線程與非守護線程的詳解

    這篇文章主要介紹了java守護線程與非守護線程,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • springboot集成Kaptcha實現(xiàn)圖片驗證碼過程

    springboot集成Kaptcha實現(xiàn)圖片驗證碼過程

    這篇文章主要介紹了springboot集成Kaptcha實現(xiàn)圖片驗證碼過程,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-06-06

最新評論

石林| 丰都县| 庆城县| 图片| 宣化县| 陇川县| 大方县| 苏尼特右旗| 大城县| 吴川市| 渭南市| 耿马| 赫章县| 丹寨县| 怀化市| 临安市| 儋州市| 明水县| 神木县| 绥宁县| 临潭县| 北安市| 隆尧县| 乐平市| 象山县| 留坝县| 敖汉旗| 利川市| 马龙县| 石狮市| 龙州县| 保德县| 东方市| 武城县| 平谷区| 长春市| 郸城县| 日土县| 恩平市| 甘洛县| 蚌埠市|