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

Java 互斥鎖實戰範例:synchronized 關鍵字與物件鎖機制深度解析

     編輯:Java 相關知識

互斥鎖是多線程環境下保護共享資源的基礎同步機制。本文通過計數器競態問題,示範 synchronized 關鍵字的正確使用姿勢與常見誤區。

問題複現:無鎖計數器的競態條件

public class LockDemo {
    private int counter = 0;
    
    public void increase() {
        for (int k = 0; k < 10; k++) {
            counter++;  // 非原子操作:讀取-修改-寫入
        }
        System.out.println(Thread.currentThread().getName() + " 計數器值:" + counter);
    }
    
    public static void main(String[] args) {
        LockDemo demo = new LockDemo();
        Thread[] threads = new Thread[1000];
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(demo::increase);
            threads[i].start();
        }
    }
}

預期:最終計數器 = 1000 × 10 = 10,000
實際:遠小於 10,000,每次運行結果不同

解決方案:synchronized 互斥鎖

鎖物件選擇原則

  • 實例方法:鎖 this 或私有實例欄位
  • 靜態方法:鎖 ClassName.class 或私有靜態欄位
  • 共享資源:所有線程必須鎖定同一個物件

正確實作

public class LockDemo {
    private static final Object LOCK = new Object(); // 靜態確保單一鎖物件
    private int counter = 0;
    
    public void increase() {
        synchronized (LOCK) {  // 關鍵區段
            for (int k = 0; k < 10; k++) {
                counter++;
            }
            System.out.println(Thread.currentThread().getName() + " 計數器值:" + counter);
        }
    }
    // main 方法同前
}

輸出:每個線程輸出的計數器值嚴格遞增,最終穩定為 10,000。

進階:鎖粒度與效能考量

鎖範圍優點缺點適用場景
方法級 synchronized簡單、不易出錯並發度低低競爭、簡單邏輯
程式碼塊級精確控制臨界區大小需仔細設計鎖物件高競爭、複雜邏輯
分段鎖高並發度實作複雜ConcurrentHashMap 風格

常見錯誤與最佳實踐

❌ 錯誤:每次建立新鎖物件

// 錯誤:每次呼叫 new Object(),線程鎖定不同物件,無互斥效果
synchronized (new Object()) { counter++; }

❌ 錯誤:鎖定非共享物件

// 錯誤:字串常量池可能導致非預期共享
synchronized ("lock") { ... }

✅ 最佳實踐

// 實例鎖
private final Object lock = new Object();

// 靜態鎖
private static final Object STATIC_LOCK = new Object();

// 使用 java.util.concurrent.locks.ReentrantLock 獲得更多控制
private final Lock lock = new ReentrantLock();
public void increase() {
    lock.lock();
    try {
        // 臨界區
    } finally {
        lock.unlock(); // 必須在 finally 中釋放
    }
}

JVM 底層機制補充

  • synchronized 編譯為 monitorenter/monitorexit 位元組碼指令
  • JDK 6+ 引入鎖升級:偏向鎖 → 輕量級鎖 → 重量級鎖
  • 逃逸分析可消除無競爭同步(標量替換)
  • 非公平鎖預設,吞吐優先;公平鎖需 ReentrantLock(true)

掌握互斥鎖核心在於:識別共享可變狀態、選擇正確鎖物件、最小化臨界區。這是構建正確並發程式的基石技能。

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