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

Java 線程同步深度解析:ReentrantLock、Condition 與 synchronized 實戰對比

     編輯:Java 相關知識

競爭條件

多線程同時修改共享資源時,若操作非原子性,會導致資料不一致。典型例子:銀行轉帳三步驟(讀餘額→扣款→寫回),中間被打斷會造成金額錯誤。

ReentrantLock 核心用法

基本加解鎖

ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
    // 臨界區程式碼
} finally {
    lock.unlock(); // 必須在 finally 中釋放
}

鐵律:unlock() 必須放在 finally,否則異常導致鎖永不釋放造成死鎖。

可重入性

同一線程可多次 lock(),內部計數器記錄次數,需對應次數 unlock()。遞迴或循環中使用安全。

Condition 物件:精確控制線程等待/喚醒

問題:鎖住自己

若在鎖內直接 if (a > b) a.set(b-1);,條件不滿足時線程持續持有鎖,其他線程無法進入修改 a、b,導致條件永不滿足——「自己鎖死自己」。

解決:await / signalAll

ReentrantLock lock = new ReentrantLock();
Condition cd = lock.newCondition();

lock.lock();
try {
    while (!(a > b)) {
        cd.await(); // 釋放鎖並阻塞,等待喚醒
    }
    a.set(b - 1);
    cd.signalAll(); // 喚醒所有等待此 Condition 的線程
} finally {
    lock.unlock();
}

標準模式:while (條件不滿足) condition.await(); —— 喚醒後重新檢查,防止虛假喚醒。

死鎖風險

所有線程都在 await() 且無人 signalAll() → 死鎖。務必確保有線程會喚醒其他線程。

synchronized 關鍵字

語法簡化

// 等價於 ReentrantLock + Condition
public synchronized void function() {
    while (!(a > b)) wait(); // 釋放鎖並等待
    a.set(b - 1);
    notifyAll(); // 喚醒所有等待此物件鎖的線程
}
  • 每個物件天然有一把內部鎖
  • wait() ≈ await(),notifyAll() ≈ signalAll()
  • 靜態 synchronized 鎖住 Class 物件

內部鎖限制

  1. 無法中斷正在等待鎖的線程
  2. 無法設定獲取鎖超時時間
  3. 每把鎖只能有一個條件(無法像 Condition 建立多個)

選型建議

  1. 優先使用 java.util.concurrent 高層工具(如 BlockingQueue、Semaphore)
  2. synchronized 滿足需求時優先用(簡單、JVM 優化好)
  3. 需要 ReentrantLock 進階功能(公平鎖、多條件、可中斷、超時)時再使用
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved