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

Java 多線程同步核心:synchronized 關鍵字深度解析與實戰

     編輯:Java 相關知識

為什麼需要線程同步?

多線程環境下,共享資源(變量、文件、數據庫連接等)若無協調機制,會導致 數據競態條件,產生髒讀、丟失更新等嚴重問題。Java 提供 synchronized 關鍵字作為內建鎖機制,確保同一時刻僅有一個線程能訪問受保護的代碼區域。

synchronized 三大核心規則

理解這三條規則是掌握 synchronized 的鑰匙
規則描述關鍵點
① 同一對象的同步方法/塊互斥 線程 A 進入對象 obj 的同步區域時,線程 B 嘗試進入 obj 的同一同步區域將阻塞 鎖針對「對象實例」
② 同步不阻塞非同步代碼 線程 A 持有 obj 鎖時,線程 B 仍可自由訪問 obj 的非同步方法/塊 非同步區域無鎖保護
③ 同一對象的不同同步區域互斥 線程 A 在 obj 的同步方法 1,線程 B 嘗試進入 obj 的同步方法 2/塊 → 阻塞 同一對象只有一把鎖

實例鎖 vs 類鎖(全局鎖)

實例鎖

// 同步實例方法
public synchronized void instanceMethod() { ... }

// 同步代碼塊(鎖當前實例)
public void blockSync() {
    synchronized(this) { ... }
}

// 同步代碼塊(鎖指定實例)
public void customLock(Object lock) {
    synchronized(lock) { ... }
}

特點:不同實例互不干擾,適合保護實例級共享狀態。

類鎖(靜態同步)

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

// 同步代碼塊(鎖 Class 對象)
public static void classBlockSync() {
    synchronized(MyClass.class) { ... }
}

特點:全進程唯一,所有實例共享同一把鎖,適合保護類級共享狀態(如靜態變量、單例初始化)。

經典對比演示

案例 1:Runnable 共享實例 → 串行執行

class SharedTask implements Runnable {
    public void run() {
        synchronized(this) { // 鎖住 SharedTask 實例
            for (int i=0; i<5; i++) {
                System.out.println(Thread.currentThread().getName() + " - " + i);
                sleep(100);
            }
        }
    }
}

public class Demo1 {
    public static void main(String[] args) {
        SharedTask task = new SharedTask(); // 單一實例
        new Thread(task, "T1").start();
        new Thread(task, "T2").start();
    }
}
// 輸出:T1 連續打印 0-4,然後 T2 打印 0-4(串行)

案例 2:Thread 繼承 → 並行執行

class MyThread extends Thread {
    public void run() {
        synchronized(this) { // 鎖住各自的 Thread 實例(不同對象!)
            for (int i=0; i<5; i++) { ... }
        }
    }
}

public class Demo2 {
    public static void main(String[] args) {
        new MyThread("T1").start();
        new MyThread("T2").start();
    }
}
// 輸出:T1、T2 交替打印(並行)
核心差異: `synchronized(this)` 中的 `this` 指向當前類的實例。案例 1 共享同一 Runnable 實例;案例 2 每個 Thread 都是獨立實例。

同步方法 vs 同步代碼塊:性能與靈活性權衡

// 同步整個方法 - 粒度粗,簡單但可能降低並發度
public synchronized void coarseSync() { ... }

// 同步代碼塊 - 粒度細,僅鎖保護必要區域
public void fineSync() {
    // 非同步預處理...
    synchronized(this) {
        // 核心臨界區
    }
    // 非同步後處理...
}

基準測試 顯示:細粒度同步塊通常比整個方法同步快 2-3 倍(視臨界區佔比而定)。

進階模式:雙重檢查鎖定(DCL)實現線程安全單例

public class Singleton {
    private static volatile Singleton instance; // volatile 禁止指令重排
    
    private Singleton() {}
    
    public static Singleton getInstance() {
        if (instance == null) {                 // 第一次檢查(無鎖,高性能)
            synchronized (Singleton.class) {    // 類鎖
                if (instance == null) {         // 第二次檢查(防重複創建)
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
  • volatile 確保:寫入 instance 時,對象構造完成前不對其他線程可見
  • JDK 1.5+ 修復了內存模型,DCL 才真正線程安全

最佳實踐總結

  1. 縮小鎖範圍:優先用同步塊鎖住最小臨界區
  2. 避免鎖活躍對象:勿用 `synchronized(this)` 鎖可能被外部代碼持有的對象
  3. 私有最終鎖對象:`private final Object lock = new Object();` 防止鎖對象被竄改
  4. 考慮 ReentrantLock:需可中斷、公平鎖、條件變量等高級特性時
  5. 死鎖預防:固定獲取鎖順序、使用 `tryLock` 超時機制

總結

synchronized 是 Java 並發編程的基石。深入理解其鎖機制、作用域規則與實例/類鎖區別,配合 volatile、DCL 等進階模式,能構建高效、線程安全的並發系統。實戰中應遵循「最小鎖範圍、明確鎖所有權」原則,必要時升級至 `java.util.concurrent` 套件。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved