多線程同時修改共享資源時,若操作非原子性,會導致資料不一致。典型例子:銀行轉帳三步驟(讀餘額→扣款→寫回),中間被打斷會造成金額錯誤。
ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
// 臨界區程式碼
} finally {
lock.unlock(); // 必須在 finally 中釋放
}
鐵律:unlock() 必須放在 finally,否則異常導致鎖永不釋放造成死鎖。
同一線程可多次 lock(),內部計數器記錄次數,需對應次數 unlock()。遞迴或循環中使用安全。
若在鎖內直接 if (a > b) a.set(b-1);,條件不滿足時線程持續持有鎖,其他線程無法進入修改 a、b,導致條件永不滿足——「自己鎖死自己」。
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() → 死鎖。務必確保有線程會喚醒其他線程。
// 等價於 ReentrantLock + Condition
public synchronized void function() {
while (!(a > b)) wait(); // 釋放鎖並等待
a.set(b - 1);
notifyAll(); // 喚醒所有等待此物件鎖的線程
}
wait() ≈ await(),notifyAll() ≈ signalAll()synchronized 鎖住 Class 物件Condition 建立多個)java.util.concurrent 高層工具(如 BlockingQueue、Semaphore)synchronized 滿足需求時優先用(簡單、JVM 優化好)ReentrantLock 進階功能(公平鎖、多條件、可中斷、超時)時再使用