首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 多線程 synchronized 關鍵字深度解析:鎖機制、方法鎖、塊鎖、靜態鎖與實戰陷阱

     編輯:Java 相關知識

前言

synchronized 是 Java 並發編程基石,理解其鎖對象、作用域、重入性、與靜態方法交互細節,是寫出正確高效多線程代碼的前提。本文通過四個遞進實例,徹底講透 synchronized 核心機制。

一、核心概念速覽

  • 鎖對象:每個 Java 對象天生擁有一把監視器鎖
  • 同步方法:鎖住 this (實例方法) 或 Class 對象 (靜態方法)
  • 同步塊:synchronized(obj) { ... } 手動指定鎖對象,粒度更細
  • 重入性:同一線程可重複獲得同一把鎖
  • 異常釋放:方法正常返回或拋異常均自動釋放鎖

二、實例 1:實例方法加鎖 vs 不加鎖

class Example {
    public synchronized void execute() { // 加鎖
        for (int i=0; i<10; i++) {
            sleep(500);
            System.out.println("Hello: " + i);
        }
    }
}
class Thread1 extends Thread {
    Example ex;
    public void run() { ex.execute(); }
}
public static void main(String[] args) {
    Example ex = new Example();
    new Thread1(ex).start();
    new Thread1(ex).start(); // 共享同一 Example 實例
}

執行結果對比

  • 無 synchronized:兩線程交替輸出 Hello: 0, Hello: 0, Hello: 1, Hello: 1... (並發)
  • 有 synchronized:先完整輸出 0-9,再輸出第二組 0-9 (串行)

結論:實例同步方法鎖住 this,同一實例同一時間只能有一個線程進入任意同步方法。

三、實例 2:同一實例的多個同步方法互斥

class Example {
    public synchronized void execute() { for(i..) print("Hello:"+i); }
    public synchronized void execute2() { for(i..) print("World:"+i); }
}
new Thread1(ex).start(); // 呼叫 execute()
new Thread2(ex).start(); // 呼叫 execute2()

結果

依然串行:先跑完 execute() 全部循環,再跑 execute2()。

鐵律:一個對象同一時間只能被一個線程持有其監視器鎖,該對象所有同步方法/塊互斥。

四、實例 3:靜態同步方法鎖住 Class 對象

class Example {
    public static synchronized void execute() { ... }
    public static synchronized void execute2() { ... }
}
Example ex1 = new Example();
Example ex2 = new Example(); // 不同實例
new Thread1(ex1).start(); // 呼叫 Example.execute()
new Thread2(ex2).start(); // 呼叫 Example.execute2()

結果

依然串行!因為靜態同步方法鎖住 Example.class,所有實例共享同一把 Class 鎖。

五、實例 4:同步塊精細控制鎖範圍

class Example {
    private final Object lock = new Object(); // 專用鎖對象
    public void execute() {
        synchronized (lock) { // 僅鎖住關鍵區
            for(i..) print("Hello:"+i);
        }
        // 這裡代碼可並發執行
    }
    public void execute2() {
        synchronized (lock) { ... }
    }
}

優勢

  • 鎖粒度可控:僅保護共享資源訪問區,非關鍵代碼並發執行
  • 可用不同鎖對象分離不同資源:synchronized(lockA) vs synchronized(lockB)
  • 等價於 synchronized(this) { 方法體所有代碼 },但更靈活

六、常見陷阱與最佳實踐

陷阱後果修正
鎖住 String 常量 / 包裝類字串常量池/緩存導致非預期共享鎖用 new Object() 專用鎖對象
同步塊中調用耗時操作 (IO、sleep)長時間持有鎖,嚴重降低並發度縮小同步塊範圍,耗時操作移出
以為靜態/實例同步方法不互斥實則鎖對象不同 (this vs Class),互不干擾明確區分鎖對象,需跨類同步用共享鎖
死鎖多線程循環等待對方鎖固定獲取鎖順序、用 tryLock、避免巢狀鎖

七、總結

  1. 實例同步方法 → 鎖 this
  2. 靜態同步方法 → 鎖 Class 對象
  3. 同步塊 → 鎖括號內指定對象,粒度最細
  4. 同一把鎖保護的所有代碼區 (方法/塊) 互斥執行
  5. 異常、返回均自動釋放鎖,無需手動 unlock
  6. 高併發場景優先考慮 ReentrantLock、StampedLock、無鎖資料結構
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved