多線程環境下,線程間如何協作處理共享資源是並發編程的核心課題。本文將從傳統的 wait/notify 機制講起,逐步揭示其在多生產者多消費者場景下的缺陷,並引入 JDK 1.5 引入的 Lock 和 Condition 作為現代解決方案。
wait():釋放鎖並進入等待狀態,線程被存入等待隊列notify():隨機喚醒一個等待線程notifyAll():喚醒所有等待線程關鍵規則:這些方法必須在同步塊或同步方法中調用,因為它們操作的是當前對象的監視器鎖。
因為監視器本質上就是鎖,任意對象都可作為鎖,所以監視器方法必須定義在所有對象的父類 Object 中。
class Resource {
private String name;
private int count = 1;
private boolean flag = false;
public synchronized void set(String name) {
if (flag) {
try { this.wait(); } catch (InterruptedException e) {}
}
this.name = name + count++;
System.out.println(Thread.currentThread().getName() + " 生產: " + this.name);
flag = true;
notify();
}
public synchronized void out() {
if (!flag) {
try { this.wait(); } catch (InterruptedException e) {}
}
System.out.println(Thread.currentThread().getName() + " 消費: " + this.name);
flag = false;
notify();
}
}
單生產者單消費者時運行完美,但擴展到多生產者多消費者會出現問題。
當多個生產線程被 wait() 後,喚醒時不再檢查條件直接執行,導致重複生產。
解決:將 if 改為 while 循環檢查條件。
喚醒的是同類線程(如喚醒另一個生產者),雙方都在等待,無線程能推進業務。
解決:使用 notifyAll() 確保至少喚醒一個對方線程。
if:只檢查一次,不安全while:循環檢查,解決虛假喚醒notifyAll:喚醒所有,解決死鎖JDK 1.5 java.util.concurrent.locks 包提供了更靈活的鎖機制:
class Resource1 {
private String name;
private int count = 1;
private boolean flag = false;
private Lock lock = new ReentrantLock();
private Condition producerCon = lock.newCondition();
private Condition consumerCon = lock.newCondition();
public void set(String name) {
lock.lock();
try {
while (flag) producerCon.await();
this.name = name + count++;
System.out.println(Thread.currentThread().getName() + " 生產: " + this.name);
flag = true;
consumerCon.signal(); // 精準喚醒消費者
} finally {
lock.unlock();
}
}
public void out() {
lock.lock();
try {
while (!flag) consumerCon.await();
System.out.println(Thread.currentThread().getName() + " 消費: " + this.name);
flag = false;
producerCon.signal(); // 精準喚醒生產者
} finally {
lock.unlock();
}
}
}
| 傳統機制 | Lock/Condition |
|---|---|
| synchronized | Lock.lock() / unlock() |
| wait() | Condition.await() |
| notify() | Condition.signal() |
| notifyAll() | Condition.signalAll() |
class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
final Object[] items = new Object[100];
int putptr, takeptr, count;
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length) notFull.await();
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
notEmpty.signal();
} finally { lock.unlock(); }
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0) notEmpty.await();
Object x = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
notFull.signal();
return x;
} finally { lock.unlock(); }
}
}
Lock/Condition 將鎖和監視器解耦,支持多條件變量,實現了「生產者喚醒消費者、消費者喚醒生產者」的精準控制,徹底解決了傳統機制的效率與死鎖問題,是高並發場景下的首選方案。