早期嘗試用 volatile boolean done 標記控制線程結束,雖可行但會導致 CPU 空轉(忙等待),浪費資源。Java 物件內建的 wait()/notify() 機制提供了高效的線程協作方案。
synchronized 方法/塊中調用synchronized 解決競態條件,兩者配合使用private boolean done = true; // 無需 volatile,因同步塊保護
public synchronized void run() {
while (true) {
try {
if (done) {
wait(); // 條件不滿足,釋放鎖進入等待
} else {
repaint();
wait(100); // 定時喚醒重繪
}
} catch (InterruptedException e) { return; }
}
}
public synchronized void setDone(boolean b) {
done = b;
if (timer == null) { timer = new Thread(this); timer.start(); }
if (!done) notify(); // 條件滿足,通知等待線程
}
wait() 釋放同步鎖,允許其他線程進入同步塊修改條件並調用 notify()sleep() 持有鎖不釋放 → setDone() 永無法獲取鎖 → 死鎖雖 run() 無限循環,但 wait() 釋放鎖,避免了「鎖永不釋放」的風險。
若 notify() 時無線程等待,通知丟失。後續 wait() 必須等待下一次通知 → 必須在循環中檢查條件。
「檢查條件 → 調用 wait()」與「設置條件 → 調用 notify()」之間存在競態,必須在同一把鎖保護下完成,確保原子性。
wait() 返回不保證條件成立,必須重新檢查條件(循環模式)notify() 隨機喚醒一個,其他線程需重新等待notifyAll()(實際仍受鎖限制,同一時刻僅一線程執行)private Object doneLock = new Object();
public void run() {
synchronized (doneLock) {
while (true) {
if (done) doneLock.wait();
else { repaint(); doneLock.wait(100); }
}
}
}
public void setDone(boolean b) {
synchronized (doneLock) {
done = b;
if (!done) doneLock.notify();
}
}
優勢:將「條件鎖」與「物件鎖」分離,提高並發吞吐。
private Lock lock = new ReentrantLock();
private Condition cv = lock.newCondition();
public void run() {
lock.lock();
try {
while (true) {
if (done) cv.await();
else { nextCharacter(); cv.await(getPauseTime(), TimeUnit.MILLISECONDS); }
}
} catch (InterruptedException e) { return; }
finally { lock.unlock(); }
}
public void setDone(boolean b) {
lock.lock();
try {
done = b;
if (!done) cv.signal();
} finally { lock.unlock(); }
}
Lock 內部已佔用 wait/notify,不可混用Lock 可創建多個 Condition,實現精準喚醒(如「非空」與「非滿」分離)awaitUntil()、awaitNanos() 等支援絕對/相對超時,可中斷等待本質上 Condition 復刻了 wait/notify 語義,但提供了更靈活的控制粒度與超時語義。