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

Java synchronized從使用到底層鎖升級(jí)機(jī)制詳解

 更新時(shí)間:2026年03月25日 09:26:30   作者:weisian151  
本文深入解析Java中synchronized的底層機(jī)制與鎖升級(jí)過程,文章從基礎(chǔ)用法入手,區(qū)分實(shí)例鎖、類鎖和代碼塊鎖的使用場(chǎng)景及常見誤區(qū),感興趣的朋友跟隨小編一起看看吧

在Java并發(fā)編程中,synchronized是最基礎(chǔ)也最核心的鎖機(jī)制——它使用簡單(加個(gè)關(guān)鍵字就能保證線程安全),但底層原理卻藏著大量面試高頻考點(diǎn):

  • 只會(huì)用synchronized修飾方法,說不清楚“實(shí)例鎖”和“類鎖”的區(qū)別;
  • 被追問“synchronized底層怎么實(shí)現(xiàn)?”,只知道“Monitor”卻講不出細(xì)節(jié);
  • 分不清“偏向鎖、輕量級(jí)鎖、重量級(jí)鎖”的升級(jí)邏輯,答不出JDK 1.6的優(yōu)化點(diǎn);
  • 面試被問“為什么synchronized是可重入的?”“高并發(fā)下偏向鎖要不要關(guān)閉?”當(dāng)場(chǎng)卡殼。

本文從使用場(chǎng)景字節(jié)碼原理底層結(jié)構(gòu)鎖升級(jí)流程面試考點(diǎn)層層拆解,結(jié)合圖解、代碼案例源碼片段,徹底講透synchronized的核心邏輯:
? 3種使用方式+字節(jié)碼層面解析,分清實(shí)例鎖/類鎖;
? 圖解對(duì)象頭(Mark Word)結(jié)構(gòu),看懂鎖狀態(tài)的存儲(chǔ)邏輯;
? 動(dòng)態(tài)拆解“無鎖→偏向鎖→輕量級(jí)鎖→重量級(jí)鎖”升級(jí)全過程;
? JDK 1.6鎖優(yōu)化:自適應(yīng)自旋、鎖消除、鎖粗化(面試加分點(diǎn));
? 10+高頻面試題標(biāo)準(zhǔn)答案(直接背)。

?? 核心一句話
synchronized基于對(duì)象監(jiān)視器(Monitor)實(shí)現(xiàn),JDK 1.6為解決性能問題引入“鎖升級(jí)”機(jī)制,從無鎖逐步升級(jí)為偏向鎖、輕量級(jí)鎖、重量級(jí)鎖,僅在高競爭場(chǎng)景下才會(huì)觸發(fā)重量級(jí)鎖(依賴OS互斥量),大幅提升低競爭場(chǎng)景的性能。

?? 面試金句先記牢

  • synchronized的三種用法:實(shí)例鎖(this)、類鎖(Class對(duì)象)、代碼塊鎖(任意對(duì)象);
  • 鎖升級(jí)是不可逆的單向過程(偏向鎖→輕量級(jí)鎖→重量級(jí)鎖),核心依賴對(duì)象頭Mark Word存儲(chǔ)鎖狀態(tài);
  • JDK 1.6優(yōu)化后,synchronized不再是“重量級(jí)鎖”的代名詞,低競爭場(chǎng)景下性能接近CAS;
  • synchronized的可重入性依賴Monitor的計(jì)數(shù)器機(jī)制,每加鎖一次計(jì)數(shù)器+1,解鎖一次-1,為0時(shí)釋放鎖。
  • 字節(jié)碼層面通過monitorentermonitorexit指令實(shí)現(xiàn);

一、基礎(chǔ)篇:synchronized的3種使用方式(避坑版)

synchronized的使用看似簡單,但90%的開發(fā)者會(huì)混淆“實(shí)例鎖”和“類鎖”,導(dǎo)致線程安全問題。先從使用方式入手,夯實(shí)基礎(chǔ)。

1.1 修飾實(shí)例方法(實(shí)例鎖)

鎖對(duì)象是當(dāng)前實(shí)例(this),不同實(shí)例之間的鎖互不干擾。

public class SyncInstanceMethod {
    // 實(shí)例鎖:鎖對(duì)象是this
    public synchronized void doSomething() {
        System.out.println("實(shí)例方法鎖:" + Thread.currentThread().getName());
        try {
            Thread.sleep(1000); // 模擬耗時(shí)操作
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
    public static void main(String[] args) {
        SyncInstanceMethod instance1 = new SyncInstanceMethod();
        SyncInstanceMethod instance2 = new SyncInstanceMethod();
        // 線程1:調(diào)用instance1的同步方法(鎖instance1)
        new Thread(() -> instance1.doSomething(), "線程1").start();
        // 線程2:調(diào)用instance2的同步方法(鎖instance2)→ 不會(huì)阻塞
        new Thread(() -> instance2.doSomething(), "線程2").start();
    }
}

結(jié)果:線程1和線程2同時(shí)執(zhí)行(不同實(shí)例,鎖不互斥)。

1.2 修飾靜態(tài)方法(類鎖)

鎖對(duì)象是類的Class對(duì)象(如SyncStaticMethod.class),所有實(shí)例共享同一把鎖。

public class SyncStaticMethod {
    // 類鎖:鎖對(duì)象是SyncStaticMethod.class
    public static synchronized void doStaticSomething() {
        System.out.println("靜態(tài)方法鎖:" + Thread.currentThread().getName());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
    public static void main(String[] args) {
        SyncStaticMethod instance1 = new SyncStaticMethod();
        SyncStaticMethod instance2 = new SyncStaticMethod();
        // 線程1:調(diào)用靜態(tài)方法(鎖Class對(duì)象)
        new Thread(() -> instance1.doStaticSomething(), "線程1").start();
        // 線程2:調(diào)用靜態(tài)方法(同一把類鎖)→ 阻塞
        new Thread(() -> instance2.doStaticSomething(), "線程2").start();
    }
}

結(jié)果:線程2等待線程1執(zhí)行完后才執(zhí)行(類鎖全局唯一)。

1.3 修飾代碼塊(自定義鎖對(duì)象)

靈活指定鎖對(duì)象,是最常用的方式(縮小鎖范圍,提升性能)。

public class SyncCodeBlock {
    // 自定義鎖對(duì)象(推薦使用final,避免鎖對(duì)象被修改)
    private final Object lock = new Object();
    // 類鎖的代碼塊寫法
    private static final Object classLock = new Object();
    public void doBlockSomething() {
        // 鎖自定義對(duì)象
        synchronized (lock) {
            System.out.println("代碼塊鎖(實(shí)例):" + Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
    public static void doStaticBlockSomething() {
        // 鎖Class對(duì)象(等價(jià)于靜態(tài)方法鎖)
        synchronized (SyncCodeBlock.class) {
            System.out.println("代碼塊鎖(類):" + Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
    public static void main(String[] args) {
        SyncCodeBlock instance = new SyncCodeBlock();
        // 同一實(shí)例的代碼塊鎖互斥
        new Thread(() -> instance.doBlockSomething(), "線程1").start();
        new Thread(() -> instance.doBlockSomething(), "線程2").start();
    }
}

1.4 核心避坑點(diǎn)

錯(cuò)誤用法問題本質(zhì)正確做法
鎖字符串常量(如synchronized ("lock")字符串常量池復(fù)用,導(dǎo)致不同業(yè)務(wù)共享同一把鎖使用自定義final Object作為鎖對(duì)象
鎖局部變量(如synchronized (new Object())每次創(chuàng)建新對(duì)象,鎖失效(線程不互斥)鎖成員變量或類變量
實(shí)例鎖和類鎖混用兩者是不同鎖對(duì)象,無法保證線程安全明確鎖粒度,統(tǒng)一使用實(shí)例鎖或類鎖

二、原理篇:字節(jié)碼+Monitor,看懂synchronized的底層實(shí)現(xiàn)

要理解鎖升級(jí),先搞懂synchronized最基礎(chǔ)的實(shí)現(xiàn)邏輯——字節(jié)碼指令和Monitor機(jī)制。

2.1 字節(jié)碼層面:monitorenter/monitorexit指令

編譯后的synchronized代碼塊會(huì)生成monitorentermonitorexit指令,修飾方法則會(huì)在方法表中標(biāo)記ACC_SYNCHRONIZED。

代碼示例

public class SyncByteCode {
    private final Object lock = new Object();
    public void test() {
        synchronized (lock) {
            System.out.println("synchronized代碼塊");
        }
    }
}

核心字節(jié)碼(關(guān)鍵部分)

執(zhí)行:

javap -c SyncByteCode.class

字節(jié)碼信息如下:

// synchronized (lock) 對(duì)應(yīng)的字節(jié)碼
0 aload_0
1 getfield      #2                  // 獲取lock對(duì)象
4 dup
5 astore_1
6 monitorenter  // 進(jìn)入Monitor,獲取鎖
7 getstatic     #3                  // System.out
10 ldc           #4                  // 字符串"synchonized代碼塊"
12 invokevirtual #5                  // 執(zhí)行println
15 aload_1
16 monitorexit   // 退出Monitor,釋放鎖
17 goto          25
20 astore_2
21 aload_1
22 monitorexit   // 異常時(shí)的monitorexit(保證鎖釋放)
23 aload_2
24 athrow
25 return

指令解析

  • monitorenter:嘗試獲取對(duì)象的Monitor所有權(quán),成功則計(jì)數(shù)器+1,失敗則線程阻塞;
  • monitorexit:釋放Monitor所有權(quán),計(jì)數(shù)器-1,當(dāng)計(jì)數(shù)器為0時(shí),完全釋放鎖;
  • 編譯器會(huì)生成兩個(gè)monitorexit:一個(gè)正常退出,一個(gè)異常退出(保證鎖最終釋放,避免死鎖)。

2.2 Monitor(對(duì)象監(jiān)視器):synchronized的核心依賴

每個(gè)Java對(duì)象都關(guān)聯(lián)一個(gè)Monitor(C++實(shí)現(xiàn),ObjectMonitor類),其核心結(jié)構(gòu)如下:

  • _Owner:當(dāng)前持有鎖的線程,初始為null;
  • _EntryList:未獲取鎖的線程進(jìn)入該隊(duì)列,處于BLOCKED狀態(tài);
  • _WaitSet:調(diào)用wait()的線程進(jìn)入該隊(duì)列,處于WAITING狀態(tài);
  • _count:可重入計(jì)數(shù)器,初始為0,每加鎖一次+1,解鎖一次-1。

2.3 基礎(chǔ)實(shí)現(xiàn)流程

  1. 線程執(zhí)行monitorenter時(shí),嘗試將Monitor的_Owner設(shè)為當(dāng)前線程:
    • 成功:_count+1,執(zhí)行同步代碼塊;
    • 失?。哼M(jìn)入_EntryList阻塞,等待鎖釋放。
  2. 線程執(zhí)行monitorexit時(shí),_count-1:
    • _count=0:釋放鎖(_Owner設(shè)為null),喚醒_EntryList中的線程競爭鎖;
    • _count>0:僅減少計(jì)數(shù)器(可重入特性)。

三、核心篇:對(duì)象頭Mark Word與鎖狀態(tài)存儲(chǔ)

鎖升級(jí)的核心是對(duì)象頭(Object Header),其中的Mark Word字段存儲(chǔ)了鎖狀態(tài)、線程ID等關(guān)鍵信息,先看懂Mark Word的結(jié)構(gòu),才能理解鎖升級(jí)。

3.1 Java對(duì)象的內(nèi)存布局

每個(gè)Java對(duì)象在內(nèi)存中分為3部分:

┌─────────────────────────────────────────────────────────┐
│                    Object Header (對(duì)象頭)               │
│ ├─────────────────────────────────────────────────────┤ │
│ │ Mark Word (標(biāo)記字段):存儲(chǔ)鎖狀態(tài)、哈希值、線程ID等  │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ Klass Pointer (類型指針):指向類的Class對(duì)象         │ │
│ └─────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────┤
│ Instance Data (實(shí)例數(shù)據(jù)):存儲(chǔ)對(duì)象的成員變量            │
├─────────────────────────────────────────────────────────┤
│ Padding (對(duì)齊填充):保證對(duì)象大小為8字節(jié)的整數(shù)倍         │
└─────────────────────────────────────────────────────────┘
  • Mark Word:核心字段,長度為32位(32位JVM)或64位(64位JVM),動(dòng)態(tài)存儲(chǔ)不同信息(根據(jù)鎖狀態(tài));
  • Klass Pointer:默認(rèn)占4字節(jié)(32位)/8字節(jié)(64位),指向?qū)ο蟮念愒獢?shù)據(jù);
  • Padding:僅為內(nèi)存對(duì)齊,無業(yè)務(wù)意義。

3.2 Mark Word的詳細(xì)布局(64位VM為例)

Mark Word在不同鎖狀態(tài)下存儲(chǔ)的內(nèi)容完全不同 :

鎖狀態(tài)25bit31bit1bit4bit1bit(偏向鎖)2bit(鎖標(biāo)志)
無鎖unusedhashCodeunused分代年齡001
偏向鎖threadId(54bit)epoch分代年齡101
輕量級(jí)鎖指向棧中鎖記錄(Lock Record)的指針(62bit)00
重量級(jí)鎖指向Monitor對(duì)象的指針(62bit)10
GC標(biāo)記空(不存信息)11

?? 關(guān)鍵:

  1. 鎖狀態(tài)由最后2位標(biāo)識(shí):01=無鎖/偏向鎖,00=輕量級(jí)鎖,10=重量級(jí)鎖,11=GC標(biāo)記;
  2. 偏向鎖和無鎖的鎖狀態(tài)都是01,通過是否偏向鎖位(第6位) 區(qū)分;
  3. Mark Word的動(dòng)態(tài)復(fù)用是鎖升級(jí)的核心基礎(chǔ)。

3.3 圖解:Mark Word狀態(tài)切換

┌─────────────────────────────────────────────────────────┐
│                      無鎖(001)                          │
│  ┌─────────────────────────────────────┐               │
│  │      hashCode(25/31bit)│  age  │ 0│ 01 │
│  └─────────────────────────────────────┘               │
│                         ↓ 第一個(gè)線程獲取鎖               │
├─────────────────────────────────────────────────────────┤
│                    偏向鎖(101)                          │
│  ┌─────────────────────────────────────┐               │
│  │      threadId(54bit)│epoch │age│ 1│ 01 │
│  └─────────────────────────────────────┘               │
│                         ↓ 第二個(gè)線程競爭                 │
├─────────────────────────────────────────────────────────┤
│                  輕量級(jí)鎖(00)                           │
│  ┌─────────────────────────────────────┐               │
│  │     指向Lock Record的指針(62bit)  │ 00 │
│  └─────────────────────────────────────┘               │
│                         ↓ 自旋失敗/競爭加劇              │
├─────────────────────────────────────────────────────────┤
│                  重量級(jí)鎖(10)                           │
│  ┌─────────────────────────────────────┐               │
│  │     指向Monitor對(duì)象的指針(62bit)  │ 10 │
│  └─────────────────────────────────────┘               │
└─────────────────────────────────────────────────────────┘

四、進(jìn)階篇:鎖升級(jí)全過程(無鎖→偏向鎖→輕量級(jí)鎖→重量級(jí)鎖)

JDK 1.5及之前,synchronized直接使用重量級(jí)鎖(依賴OS互斥量mutex),每次加鎖解鎖都需要從用戶態(tài)切換到內(nèi)核態(tài),開銷巨大,性能極差;

JDK 1.6引入“鎖升級(jí)”機(jī)制,僅在高競爭場(chǎng)景下才觸發(fā)重量級(jí)鎖,大幅提升性能。

核心前提

  • 鎖升級(jí)是單向不可逆的:無鎖 → 偏向鎖 → 輕量級(jí)鎖 → 重量級(jí)鎖;
  • 偏向鎖默認(rèn)開啟(JDK 1.6+),啟動(dòng)后有4秒延遲(可通過JVM參數(shù)關(guān)閉);
  • 鎖升級(jí)的觸發(fā)條件是線程競爭:無競爭→偏向鎖,輕度競爭→輕量級(jí)鎖,重度競爭→重量級(jí)鎖。

4.1 第一步:無鎖狀態(tài)

  • 對(duì)象剛創(chuàng)建時(shí),Mark Word存儲(chǔ)哈希值、分代年齡,是否偏向鎖位=0,鎖狀態(tài)=01;
  • 無任何線程競爭,無需加鎖。

4.2 第二步:偏向鎖(無競爭場(chǎng)景)

觸發(fā)條件

第一個(gè)線程獲取鎖,且無其他線程競爭。

核心邏輯(消除CAS,提升性能)

  1. 線程獲取鎖時(shí),JVM將Mark Word的是否偏向鎖位設(shè)為1,并把當(dāng)前線程ID寫入Mark Word;
  2. 該線程后續(xù)再次獲取鎖時(shí),只需檢查Mark Word中的線程ID是否為自身:
    • 是:直接進(jìn)入同步代碼塊(無需CAS,無需操作系統(tǒng)介入);
    • 否:觸發(fā)偏向鎖撤銷/升級(jí)。
  3. 偏向鎖的釋放不主動(dòng)釋放,僅在其他線程競爭時(shí)才會(huì)撤銷。

偏向鎖的優(yōu)勢(shì)

消除了輕量級(jí)鎖的CAS操作,是JDK 1.6對(duì)synchronized最核心的優(yōu)化——低競爭場(chǎng)景下,偏向鎖的性能接近無鎖。

4.3 第三步:輕量級(jí)鎖(輕度競爭)

觸發(fā)條件

有其他線程競爭偏向鎖,且當(dāng)前持有鎖的線程仍在執(zhí)行(未釋放)。

核心邏輯(CAS自旋,避免OS阻塞)

  1. 競爭線程嘗試通過CAS將Mark Word中的指針指向自己的棧幀中的“鎖記錄(Lock Record)”:
    • 成功:獲取輕量級(jí)鎖,鎖狀態(tài)改為00;
    • 失?。鹤孕ㄑh(huán)重試CAS),自旋次數(shù)達(dá)到閾值后升級(jí)為重量級(jí)鎖。
  2. 輕量級(jí)鎖的釋放:通過CAS將Mark Word恢復(fù)為無鎖狀態(tài),成功則釋放鎖,失敗則說明有競爭,升級(jí)為重量級(jí)鎖。

自適應(yīng)自旋(JDK 1.6優(yōu)化)

  • 自旋次數(shù)不是固定值,而是根據(jù)“前一次自旋是否成功”動(dòng)態(tài)調(diào)整:
    • 若前一次自旋成功,本次自旋次數(shù)增加(認(rèn)為大概率再次成功);
    • 若前一次自旋失敗,本次自旋次數(shù)減少(甚至直接升級(jí)為重量級(jí)鎖)。
  • 優(yōu)勢(shì):避免固定自旋次數(shù)導(dǎo)致的CPU浪費(fèi),適配不同競爭場(chǎng)景。

4.4 第四步:重量級(jí)鎖(重度競爭)

觸發(fā)條件

輕量級(jí)鎖自旋失敗,或多個(gè)線程同時(shí)競爭鎖。

核心邏輯(依賴OS互斥量,性能最差)

  1. 線程獲取鎖失敗后,放棄自旋,進(jìn)入Monitor的_EntryList隊(duì)列,由操作系統(tǒng)進(jìn)行阻塞(從用戶態(tài)切換到內(nèi)核態(tài));
  2. 持有鎖的線程釋放鎖時(shí),操作系統(tǒng)喚醒_EntryList中的線程,重新競爭鎖;
  3. 重量級(jí)鎖的鎖狀態(tài)為10,Mark Word存儲(chǔ)指向Monitor的指針。

性能瓶頸

用戶態(tài)→內(nèi)核態(tài)的切換成本極高,這也是JDK 1.5之前synchronized被詬病“重量級(jí)”的根本原因。

4.5 鎖升級(jí)流程圖解

┌─────────────┐
│    無鎖      │
│  標(biāo)志位:01   │
│  是否偏向:0  │
└──────┬──────┘
       │ 第一個(gè)線程獲取鎖
       ▼
┌─────────────┐
│   偏向鎖     │
│  標(biāo)志位:01   │
│  是否偏向:1  │
│  存儲(chǔ):線程ID │
└──────┬──────┘
       │ 另一個(gè)線程競爭
       ▼
┌─────────────┐
│  輕量級(jí)鎖    │
│  標(biāo)志位:00   │
│  存儲(chǔ):鎖記錄 │←───自旋等待
└──────┬──────┘
       │ 競爭加劇/自旋失敗
       ▼
┌─────────────┐
│  重量級(jí)鎖    │
│  標(biāo)志位:10   │
│  存儲(chǔ):Monitor│
└─────────────┘

4.6 三種鎖對(duì)比

鎖類型優(yōu)點(diǎn)缺點(diǎn)適用場(chǎng)景
偏向鎖加鎖解鎖無額外開銷(僅檢查)如果有競爭,撤銷偏向鎖有代價(jià)單線程訪問同步塊
輕量級(jí)鎖用CAS代替互斥量,性能好自旋會(huì)占用CPU線程交替執(zhí)行同步塊
重量級(jí)鎖線程阻塞不占用CPU上下文切換開銷大多線程同時(shí)競爭

五、優(yōu)化篇:JDK 1.6的其他鎖優(yōu)化

除了鎖升級(jí),JDK 1.6還引入了鎖消除、鎖粗化等優(yōu)化,進(jìn)一步提升synchronized的性能。

5.1 鎖消除(Lock Elimination)

核心邏輯

JVM的即時(shí)編譯器(JIT)檢測(cè)到某些鎖對(duì)象是“局部變量”,且不會(huì)被多線程訪問,直接消除鎖。

示例

public String concat(String a, String b) {
    // StringBuffer的append方法是同步的,但sb是局部變量,無多線程訪問
    StringBuffer sb = new StringBuffer();
    sb.append(a).append(b);
    return sb.toString();
}

JIT編譯時(shí)會(huì)消除StringBuffer的同步鎖,等價(jià)于:

public String concat(String a, String b) {
    StringBuilder sb = new StringBuilder(); // 非同步
    sb.append(a).append(b);
    return sb.toString();
}

5.2 鎖粗化(Lock Coarsening)

核心邏輯

將多個(gè)連續(xù)的細(xì)粒度鎖合并為一個(gè)粗粒度鎖,減少鎖的獲取/釋放次數(shù)。

示例

public void loopAdd(String str) {
    StringBuffer sb = new StringBuffer();
    for (int i = 0; i < 1000; i++) {
        sb.append(str); // 每次append都加鎖/解鎖
    }
}

JIT編譯時(shí)會(huì)將鎖粗化,等價(jià)于:

public void loopAdd(String str) {
    StringBuffer sb = new StringBuffer();
    synchronized (sb) { // 一次加鎖,覆蓋整個(gè)循環(huán)
        for (int i = 0; i < 1000; i++) {
            sb.append(str);
        }
    }
}

5.3 偏向鎖的啟用/關(guān)閉(JVM參數(shù))

參數(shù)作用
-XX:+UseBiasedLocking開啟偏向鎖(默認(rèn)開啟)
-XX:BiasedLockingStartupDelay=0關(guān)閉偏向鎖4秒啟動(dòng)延遲
-XX:-UseBiasedLocking關(guān)閉偏向鎖

?? 實(shí)戰(zhàn)建議:高并發(fā)場(chǎng)景下(如秒殺、高頻讀寫),偏向鎖會(huì)頻繁觸發(fā)撤銷/升級(jí),建議關(guān)閉(-XX:-UseBiasedLocking),直接使用輕量級(jí)鎖。

六、深度篇:synchronized的可重入性原理

synchronized是可重入鎖(同一線程可多次獲取同一把鎖),核心依賴Monitor的_count計(jì)數(shù)器:

示例代碼

public class SyncReentrant {
    public synchronized void method1() {
        System.out.println("method1");
        method2(); // 同一線程再次獲取鎖,可重入
    }
    public synchronized void method2() {
        System.out.println("method2");
    }
    public static void main(String[] args) {
        new SyncReentrant().method1();
    }
}

可重入流程

  1. 線程調(diào)用method1(),獲取鎖,Monitor的_count=1;
  2. 線程調(diào)用method2(),再次獲取同一把鎖,_count=2;
  3. method2()執(zhí)行完,釋放鎖,_count=1;
  4. method1()執(zhí)行完,釋放鎖,_count=0,完全釋放鎖。

核心價(jià)值

避免同一線程多次獲取同一把鎖時(shí)出現(xiàn)死鎖(如遞歸調(diào)用同步方法)。

七、面試高頻真題(標(biāo)準(zhǔn)答案直接背)

7.1 基礎(chǔ)必答

Q1:synchronized的三種使用方式及區(qū)別?

答案

  1. 修飾實(shí)例方法:鎖對(duì)象是this(當(dāng)前實(shí)例),不同實(shí)例鎖互不干擾;
  2. 修飾靜態(tài)方法:鎖對(duì)象是類的Class對(duì)象,所有實(shí)例共享同一把鎖;
  3. 修飾代碼塊:自定義鎖對(duì)象(如final Object),可縮小鎖范圍,提升性能。
    核心區(qū)別是鎖對(duì)象不同,導(dǎo)致鎖的粒度和作用域不同。

Q2:synchronized底層如何實(shí)現(xiàn)?

答案

  1. 字節(jié)碼層面:代碼塊生成monitorenter/monitorexit指令,方法修飾ACC_SYNCHRONIZED標(biāo)記;
  2. 底層依賴對(duì)象監(jiān)視器(Monitor):每個(gè)對(duì)象關(guān)聯(lián)一個(gè)Monitor,包含Owner、EntryList、WaitSet、計(jì)數(shù)器;
  3. JDK 1.6+引入鎖升級(jí)機(jī)制:無鎖→偏向鎖→輕量級(jí)鎖→重量級(jí)鎖,僅重度競爭時(shí)使用重量級(jí)鎖(OS互斥量),大幅提升性能。

Q3:synchronized的鎖升級(jí)過程?

答案

  1. 無鎖:對(duì)象創(chuàng)建時(shí)的初始狀態(tài),Mark Word存儲(chǔ)哈希值;
  2. 偏向鎖:第一個(gè)線程獲取鎖,Mark Word寫入線程ID,后續(xù)該線程無需CAS直接獲取鎖;
  3. 輕量級(jí)鎖:有線程競爭偏向鎖,通過CAS自旋獲取鎖,自適應(yīng)自旋失敗后升級(jí);
  4. 重量級(jí)鎖:輕量級(jí)鎖自旋失敗,線程進(jìn)入Monitor的EntryList阻塞,依賴OS互斥量實(shí)現(xiàn)。
    鎖升級(jí)是單向不可逆的,僅能從低級(jí)別向高級(jí)別升級(jí)。

7.2 深度追問

Q4:偏向鎖、輕量級(jí)鎖、重量級(jí)鎖的性能對(duì)比?

答案

  • 偏向鎖:性能最優(yōu),無CAS、無OS調(diào)用,僅檢查線程ID;
  • 輕量級(jí)鎖:性能次之,CAS自旋(用戶態(tài)),無OS阻塞;
  • 重量級(jí)鎖:性能最差,用戶態(tài)→內(nèi)核態(tài)切換,線程阻塞/喚醒成本高。
    核心原則:盡量讓鎖停留在偏向鎖/輕量級(jí)鎖階段,避免升級(jí)為重量級(jí)鎖。

Q5:為什么偏向鎖默認(rèn)有4秒延遲?

答案
JVM啟動(dòng)時(shí)會(huì)加載大量類,多線程競爭激烈,偏向鎖會(huì)頻繁撤銷/升級(jí),反而降低性能。4秒延遲是為了等JVM啟動(dòng)完成后,再開啟偏向鎖,適配低競爭場(chǎng)景。

Q6:synchronized和volatile的區(qū)別?

答案

維度synchronizedvolatile
原子性支持(保證代碼塊原子執(zhí)行)不支持(僅保證可見性)
可見性支持(釋放鎖時(shí)刷新內(nèi)存)支持(禁止指令重排+內(nèi)存刷新)
有序性支持(通過鎖保證執(zhí)行順序)支持(禁止指令重排)
鎖特性可重入鎖,有鎖升級(jí)機(jī)制無鎖,僅修飾變量
適用場(chǎng)景代碼塊/方法的線程安全變量的可見性(如狀態(tài)標(biāo)記)

Q7:高并發(fā)場(chǎng)景下,偏向鎖是否需要關(guān)閉?為什么?

答案
需要關(guān)閉。原因:

  1. 高并發(fā)場(chǎng)景下,偏向鎖的線程ID會(huì)頻繁被競爭線程修改,觸發(fā)偏向鎖撤銷;
  2. 頻繁的撤銷/升級(jí)會(huì)產(chǎn)生額外開銷,反而比直接使用輕量級(jí)鎖更慢;
  3. 關(guān)閉偏向鎖后,直接進(jìn)入輕量級(jí)鎖階段,避免撤銷開銷。

Q8:synchronized的可重入性原理是什么?

答案
依賴Monitor的計(jì)數(shù)器機(jī)制:

  1. 線程首次獲取鎖,Monitor的計(jì)數(shù)器_count=1;
  2. 同一線程再次獲取鎖,計(jì)數(shù)器+1;
  3. 線程釋放鎖,計(jì)數(shù)器-1;
  4. 計(jì)數(shù)器為0時(shí),完全釋放鎖,其他線程可競爭。
    該機(jī)制避免了同一線程多次獲取同一把鎖導(dǎo)致的死鎖。

7.3 實(shí)戰(zhàn)場(chǎng)景題

Q9:如何優(yōu)化synchronized的性能?

答案

  1. 縮小鎖范圍:使用代碼塊鎖替代方法鎖,僅鎖定核心臨界區(qū);
  2. 降低鎖粒度:如ConcurrentHashMap的分段鎖(JDK 1.7)、LongAdder的分段累加;
  3. 關(guān)閉偏向鎖:高并發(fā)場(chǎng)景下通過JVM參數(shù)關(guān)閉偏向鎖;
  4. 避免鎖競爭:如讀寫分離、使用無鎖數(shù)據(jù)結(jié)構(gòu)(Atomic系列);
  5. 利用JDK優(yōu)化:依賴JIT的鎖消除、鎖粗化。

Q10:synchronized和ReentrantLock的區(qū)別?

答案

維度synchronizedReentrantLock
底層實(shí)現(xiàn)JVM層面(Monitor)JDK層面(AQS)
鎖類型非公平鎖(默認(rèn))可公平/非公平鎖
解鎖方式自動(dòng)解鎖(monitorexit)手動(dòng)解鎖(必須finally中釋放)
功能擴(kuò)展無(僅基礎(chǔ)鎖功能)支持中斷、超時(shí)獲取鎖、條件變量
性能JDK 1.6+優(yōu)化后,低競爭接近ReentrantLock高競爭場(chǎng)景性能更優(yōu)
可重入性支持支持

八、常見誤區(qū)與避坑指南

8.1 典型誤區(qū)

? 誤區(qū)1:synchronized是重量級(jí)鎖,性能差

糾正:JDK 1.6+引入鎖升級(jí)后,低競爭場(chǎng)景下synchronized的性能接近CAS,僅重度競爭時(shí)才會(huì)升級(jí)為重量級(jí)鎖。

? 誤區(qū)2:鎖升級(jí)是可逆的

糾正:鎖升級(jí)是單向不可逆的,一旦升級(jí)為重量級(jí)鎖,不會(huì)降級(jí)為輕量級(jí)鎖/偏向鎖。

? 誤區(qū)3:偏向鎖一定提升性能

糾正:高并發(fā)場(chǎng)景下,偏向鎖的撤銷開銷會(huì)抵消其優(yōu)勢(shì),建議關(guān)閉。

? 誤區(qū)4:synchronized能保證原子性,所以無需考慮可見性

糾正:synchronized同時(shí)保證原子性、可見性、有序性——釋放鎖時(shí)會(huì)將變量刷新到主內(nèi)存,獲取鎖時(shí)會(huì)從主內(nèi)存加載最新值。

8.2 編碼規(guī)范

  1. 縮小鎖范圍:僅鎖定臨界區(qū)代碼,避免整個(gè)方法加鎖;
  2. 選擇合適的鎖對(duì)象:使用final Object作為鎖對(duì)象,避免鎖字符串常量/局部變量;
  3. 避免鎖嵌套:減少死鎖風(fēng)險(xiǎn),如必須嵌套,保證鎖的獲取順序一致;
  4. 高并發(fā)關(guān)閉偏向鎖:通過-XX:-UseBiasedLocking關(guān)閉,提升性能;
  5. 結(jié)合其他并發(fā)工具:如讀寫分離場(chǎng)景使用ReentrantReadWriteLock,替代synchronized。

總結(jié)

1. 核心知識(shí)點(diǎn)速記口訣

sync有三種,實(shí)例靜態(tài)塊,
對(duì)象頭Mark,鎖狀態(tài)存,
無鎖到偏向,輕量到重量,
升級(jí)不可逆,性能逐下降,
JDK1.6優(yōu)化,自旋鎖消除,
可重入計(jì)數(shù),解鎖要記清。

2. 核心要點(diǎn)回顧

  1. synchronized有三種使用方式,核心區(qū)別是鎖對(duì)象不同(實(shí)例/Class/自定義對(duì)象);
  2. 底層依賴Monitor實(shí)現(xiàn),JDK 1.6引入鎖升級(jí)機(jī)制(無鎖→偏向鎖→輕量級(jí)鎖→重量級(jí)鎖),大幅提升性能;
  3. 偏向鎖消除CAS開銷,輕量級(jí)鎖依賴自旋CAS,重量級(jí)鎖依賴OS互斥量;
  4. JDK 1.6的其他優(yōu)化:自適應(yīng)自旋、鎖消除、鎖粗化;
  5. synchronized是可重入鎖,依賴Monitor計(jì)數(shù)器實(shí)現(xiàn),同時(shí)保證原子性、可見性、有序性。

3. 實(shí)戰(zhàn)建議

  • 低競爭場(chǎng)景:依賴默認(rèn)的偏向鎖,無需額外優(yōu)化;
  • 中競爭場(chǎng)景:縮小鎖范圍,利用輕量級(jí)鎖的自旋優(yōu)勢(shì);
  • 高競爭場(chǎng)景:關(guān)閉偏向鎖,結(jié)合分段鎖/無鎖結(jié)構(gòu)(如Atomic系列),避免升級(jí)為重量級(jí)鎖。

寫在最后

synchronized是Java并發(fā)的基礎(chǔ),也是面試中區(qū)分“基礎(chǔ)開發(fā)者”和“資深開發(fā)者”的關(guān)鍵。很多開發(fā)者只會(huì)用synchronized保證線程安全,但說不清楚鎖升級(jí)、Mark Word、Monitor等底層原理,最終在面試中失利。

希望這篇文章能幫你吃透synchronized的核心邏輯,不僅能背出面試答案,更能理解底層原理,在實(shí)際開發(fā)中寫出高性能、無坑的并發(fā)代碼。

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

相關(guān)文章

  • Java模版引擎Freemarker

    Java模版引擎Freemarker

    FreeMarker是一個(gè)模板引擎,一個(gè)基于模板生成文本輸出的通用工具,使用純Java編寫 FreeMarker被設(shè)計(jì)用來生成HTML Web頁面,特別是基于MVC模式的應(yīng)用程序
    2016-04-04
  • 詳解Maven多模塊打包遇到的問題解決方法

    詳解Maven多模塊打包遇到的問題解決方法

    這篇文章主要介紹了詳解Maven多模塊打包遇到的問題解決方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • idea修改只讀/可寫狀態(tài)全過程

    idea修改只讀/可寫狀態(tài)全過程

    本文記錄了解決IntelliJ IDEA(打開文件只讀問題的過程,在設(shè)置中找到Editor下的Reader Mode,取消勾選選第一個(gè)可選框即可解決問題,同時(shí)提到該選項(xiàng)看起來很好看,本文僅供參考,希望能對(duì)讀者有所幫助
    2026-05-05
  • Java NIO中的零拷貝原理

    Java NIO中的零拷貝原理

    這篇文章主要介紹了Java NIO中的零拷貝原理,零拷貝即Zero-Copy,顧名思義,零拷貝是指的一種非拷貝的方式來減少IO次數(shù)的工作方式,零拷貝的作用就是減少IO,提高IO效率,需要的朋友可以參考下
    2023-11-11
  • Java程序的初始化順序,static{}靜態(tài)代碼塊和實(shí)例語句塊的使用方式

    Java程序的初始化順序,static{}靜態(tài)代碼塊和實(shí)例語句塊的使用方式

    這篇文章主要介紹了Java程序的初始化順序,static{}靜態(tài)代碼塊和實(shí)例語句塊的使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • Javabean轉(zhuǎn)換成json字符并首字母大寫代碼實(shí)例

    Javabean轉(zhuǎn)換成json字符并首字母大寫代碼實(shí)例

    這篇文章主要介紹了javabean轉(zhuǎn)成json字符并首字母大寫代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-02-02
  • SpringBoot彩色日志配置方式

    SpringBoot彩色日志配置方式

    這篇文章主要介紹了SpringBoot彩色日志配置方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-04-04
  • java 利用HttpClient PostMethod提交json數(shù)據(jù)操作

    java 利用HttpClient PostMethod提交json數(shù)據(jù)操作

    這篇文章主要介紹了java 利用HttpClient PostMethod提交json數(shù)據(jù)操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-01-01
  • java int類型二維數(shù)組實(shí)現(xiàn)“楊輝三角”的完整實(shí)例

    java int類型二維數(shù)組實(shí)現(xiàn)“楊輝三角”的完整實(shí)例

    這篇文章主要給大家介紹了關(guān)于java int類型二維數(shù)組實(shí)現(xiàn)“楊輝三角”的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • Java Hashtable機(jī)制深入了解

    Java Hashtable機(jī)制深入了解

    HashTable是jdk 1.0中引入的產(chǎn)物,基本上現(xiàn)在很少使用了,但是會(huì)在面試中經(jīng)常被問到。本文就來帶大家一起深入了解一下Hashtable,需要的可以參考一下
    2022-09-09

最新評(píng)論

和政县| 阳新县| 新干县| 扬中市| 阳高县| 荣昌县| 合肥市| 鲁甸县| 南和县| 丹巴县| 中西区| 新安县| 台江县| 时尚| 黄梅县| 吴堡县| 巫山县| 汽车| 鄂托克前旗| 九龙城区| 本溪市| 扎赉特旗| 义马市| 郓城县| 梨树县| 刚察县| 新龙县| 无棣县| 三河市| 岗巴县| 南郑县| 南澳县| 开原市| 策勒县| 沅江市| 固安县| 洪湖市| 三江| 上饶市| 北辰区| 县级市|