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

Java synchronized 關鍵字深度解析:鎖機制、JMM、死鎖與 ReentrantLock 對比

     編輯:Java 綜合教學

Java synchronized 關鍵字深度解析:從基礎用法到 JMM 與進階替代方案

synchronized 是 Java 並發編程中最基礎、最核心的同步手段。本文將系統梳理其鎖機制、作用域、死鎖風險、JVM 內存模型(JMM)交互、以及與 ThreadLocal、ReentrantLock 的對比選型。

一、核心原理:針對對象的隱式鎖

關鍵點:synchronized 鎖的是對象,而非代碼塊或方法。


class MyClass {
    synchronized void myMethod() { /* ... */ }
}
MyClass obj = new MyClass();
obj.myMethod(); // 線程需獲取 obj 實例的鎖

線程執行同步方法/塊前,必須先獲得該對象的監視器鎖(Monitor Lock)。

二、三種同步作用域與鎖對象對應關係

修飾位置鎖對象作用範圍
實例方法當前實例 this同一實例的所有同步方法互斥
靜態方法類對象 MyClass.class所有實例共享,類級別互斥
同步代碼塊 synchronized(obj)括號內對象 obj靈活控制鎖粒度

三、重要特性與常見誤區

  • 不可繼承:父類同步方法在子類中不自動同步,需顯式加 synchronized。
  • 可重入:同一線程可重複獲得同一把鎖(遞歸調用安全)。
  • 死鎖風險:多鎖不一致獲取順序導致循環等待。
    
        // 經典死鎖:鎖順序相反
        void methodA() { synchronized(lock1) { synchronized(lock2) { ... } } }
        void methodB() { synchronized(lock2) { synchronized(lock1) { ... } } }
        

四、synchronized 與 wait/notify 協作機制

wait() 釋放鎖並阻塞線程;notify() 隨機喚醒一個等待線程(需重新獲取鎖才能繼續)。

核心規則:

  1. 有 synchronized 不一定有 wait/notify。
  2. 有 wait/notify 必有 synchronized:因兩者操作的是對象鎖,調用時必須持有該鎖,否則拋 IllegalMonitorStateException。

銀行轉賬經典案例


public class Bank {
    float[] accounts;
    public synchronized void transfer(int from, int to, float amount) {
        while (accounts[from] < amount) {
            wait(); // 余額不足,釋放鎖等待
        }
        accounts[from] -= amount;
        accounts[to] += amount;
        notifyAll(); // 喚醒所有等待線程重新檢查條件
    }
}

最佳實踐:

  • 條件判斷用 while 而非 if(防止虛假喚醒)。
  • 優先用 notifyAll() 避免信號丟失。
  • 捕獲 InterruptedException 後應恢復中斷狀態:Thread.currentThread().interrupt()。

五、JMM(Java 內存模型)視角下的 synchronized

JMM 定義:

  • 主內存:所有線程共享的變量存儲區。
  • 工作內存:每線程私有,包含主存變量副本(緩存+棧)。

線程操作變量三步驟:讀取主存→工作內存計算→寫回主存。JVM 不保證步驟即時執行,導致可見性問題。

synchronized 的語義保證:

  1. 進入同步塊:將工作內存刷新到主存,清空工作內存,從主存重新讀取最新值。
  2. 退出同步塊:將工作內存寫回主存(不清空)。

這強制實現了 原子性、可見性、有序性。

六、ThreadLocal:另一條路——線程隔離而非同步

本質區別:

ThreadLocal 不是同步工具,是空間換時間的隔離方案。

七、ReentrantLock:synchronized 的功能增強版

JDK 5 引入 java.util.concurrent.locks.ReentrantLock,解決 synchronized 缺陷:

維度synchronizedThreadLocal
目的多線程共享變量同步線程間數據隔離,各自副本
機制鎖、內存屏障Map<Thread, T> 存儲副本
性能競爭激烈時低無鎖,極高
適用場景必須共享狀態線程獨享上下文(如 DB 連接、Session)

使用模板(必須在 finally 釋放鎖)


Lock lock = new ReentrantLock();
lock.lockInterruptibly();
try {
    // 臨界區
} finally {
    lock.unlock(); // 手動釋放,違反將導致死鎖
}

八、選型指南總結

  1. 低競爭、簡單同步:優先 synchronized(JVM 偏向鎖、輕量級鎖、鎖消除優化極佳)。
  2. 高競爭、需中斷/超時/多條件:使用 ReentrantLock。
  3. 線程獨享數據:使用 ThreadLocal。
  4. 只需可見性:使用 volatile。

九、核心知識點速查表

  • synchronized 修飾實例方法 → 鎖 this;修飾靜態方法 → 鎖 Class 對象;修飾代碼塊 → 鎖括號內對象。
  • 鎖不可繼承。
  • wait() 釋放鎖阻塞;notify() 隨機喚醒一線程(需獲取鎖才能運行)。
  • 有 wait/notify 必有 synchronized。
  • ReentrantLock 多 Condition、可中斷、可超時、手動解鎖、高競爭性能優。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
缺陷ReentrantLock 改進
單一 Condition支持多個 Condition(如 notEmpty/notFull)
不可中斷等待lockInterruptibly() 可響應中斷
無超時獲取tryLock(timeout) 支持超時
公平性無法控制構造函數指定公平/非公平鎖