本系列最終篇,聚焦線程安全核心問題:共享可變狀態下的數據競爭,系統講解 synchronized、Lock、wait/notify、Condition 及生產者消費者模式完整實現。
class Account { double balance = 1000; }
class Withdraw implements Runnable {
Account acc; double amount;
public void run() {
if (acc.balance >= amount) { // 檢查
sleep(10); // 模擬網絡延遲
acc.balance -= amount; // 扣款
System.out.println(Thread.currentThread().getName() + " 取款 " + amount + " 餘額 " + acc.balance);
}
}
}
// 兩線程同時取款 700,可能雙雙通過檢查,導致餘額 -400
new Thread(new Withdraw(acc, 700), "A").start();
new Thread(new Withdraw(acc, 700), "B").start();
根因:檢查-執行非原子,CPU 任意切換導致數據不一致。
public synchronized void withdraw(double amt) {
if (balance >= amt) { balance -= amt; }
}
public void withdraw(double amt) {
synchronized (this) { // 或私有 final Object lock = new Object();
if (balance >= amt) { balance -= amt; }
}
}
private final Lock lock = new ReentrantLock();
public void withdraw(double amt) {
lock.lock();
try { if (balance >= amt) balance -= amt; }
finally { lock.unlock(); }
}
鎖選擇原則:實例資源鎖 this 或專用 lock;靜態資源鎖 ClassName.class;**嚴禁**鎖字串常量、基本型別包裝對象(常量池共享導致誤鎖)。
| 特性 | Object 監視器 | Lock Condition |
|---|---|---|
| 綁定鎖 | synchronized(obj) | lock.newCondition() |
| 等待隊列 | 單一 | 多個(如 notEmpty/notFull) |
| 喚醒精度 | notify() 隨機/notifyAll() 全部 | signal() 隨機/signalAll() 全部 |
| 中斷響應 | 不支援 | await() 可響應中斷 |
| 使用難度 | 低 | 中(需手動 lock/unlock) |
class Buffer {
private int[] items = new int[10];
private int count = 0, in = 0, out = 0;
public synchronized void produce(int value) throws InterruptedException {
while (count == items.length) wait(); // 滿等待
items[in] = value; in = (in + 1) % items.length; count++;
notifyAll(); // 唤醒消費者
}
public synchronized int consume() throws InterruptedException {
while (count == 0) wait(); // 空等待
int val = items[out]; out = (out + 1) % items.length; count--;
notifyAll(); // 唤醒生產者
return val;
}
}
關鍵點:
while 而非 if 檢查條件(防虛假喚醒)。wait() 釋放鎖並進入等待集;notifyAll() 移至入口集競爭鎖。class Buffer {
private final Lock lock = new ReentrantLock();
private final Condition notFull = lock.newCondition(), notEmpty = lock.newCondition();
// produce/consume 同前,將 wait→notFull.await(), notifyAll→notEmpty.signalAll() 等
}
ThreadLocal、Atomic*、ConcurrentHashMap、BlockingQueue。Lock:可中斷、超時、公平、多條件。ArrayBlockingQueue / LinkedBlockingQueue,無需手寫。enum 或 static holder)、死鎖(固定鎖順序、tryLock 超時)。掌握同步原理與工具選型,是編寫高併發、高穩定 Java 系統的必備功力。