Object.wait()、notify()、notifyAll() 是 Java 線程間通信的基礎,必須在 synchronized 同步塊/方法中調用,否則拋出 IllegalMonitorStateException。
| 方法 | 釋放鎖 | 喚醒方式 |
|---|---|---|
| sleep() | 否 | 時間到期自動 |
| yield() | 否 | 讓出 CPU,重新競爭 |
| wait() | 是 | notify/notifyAll 或 timeout |
public class Monitor {
private boolean ready = false;
public synchronized void waitForReady() throws InterruptedException {
while (!ready) { // 必須用 while 防止虛假喚醒
wait();
}
System.out.println("收到通知,繼續執行");
}
public synchronized void setReady() {
ready = true;
notifyAll(); // 喚醒所有等待線程
}
}
public class Game implements Runnable {
private final Set players = new HashSet<>();
private boolean start = false;
public synchronized void prepare(Athlete a) throws InterruptedException {
System.out.println(a + " ready!");
while (!start) wait(); // 釋放鎖,等待發令槍
System.out.println(a + " go!");
}
public synchronized void go() { notifyAll(); }
public void ready() {
for (Athlete a : players) new Thread(a).start(); // 啟動所有選手
}
@Override public void run() {
System.out.println("Ready... Ready... Ready...");
ready();
start = true;
System.out.println("Go!");
go();
}
}
忙等待 消耗 CPU:
while (!flag) { /* 空循環 */ } // 佔用 CPU 核心
wait/notify 改進:釋放鎖,進入等待隊列,零 CPU 佔用:
synchronized (monitor) {
while (!flag) monitor.wait();
}
// 另一線程:
synchronized (monitor) {
flag = true;
monitor.notify();
}
適用於一次性延遲任務,無界阻塞隊列,元素需實作 Delayed 介面。
public class DelayedTask implements Delayed, Runnable {
private final long triggerTime; // 納秒時間戳
private final int id;
public DelayedTask(int delaySeconds) {
this.triggerTime = System.nanoTime() + TimeUnit.SECONDS.toNanos(delaySeconds);
this.id = counter++;
}
@Override public long getDelay(TimeUnit unit) {
return unit.convert(triggerTime - System.nanoTime(), TimeUnit.NANOSECONDS);
}
@Override public int compareTo(Delayed o) {
return Long.compare(this.triggerTime, ((DelayedTask)o).triggerTime);
}
@Override public void run() { System.out.println("執行任務 " + this); }
}
// 使用
DelayQueue queue = new DelayQueue<>();
queue.put(new DelayedTask(5)); // 5 秒後執行
new Thread(() -> {
while (true) queue.take().run(); // 取出並執行
}).start();
優點:任務邏輯封裝在元素內、支援任意延遲、自然排序。
缺點:需自行管理消費線程、不支援週期性任務。
JDK 1.5+ 標準方案,支援一次性延遲與固定頻率/固定延遲週期任務。
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(5);
// 1. 一次性延遲 3 秒執行
scheduler.schedule(() -> System.out.println("一次性任務"), 3, TimeUnit.SECONDS);
// 2. 固定頻率:初始延遲 1s,之後每 2s 執行一次(執行時間不計入間隔)
scheduler.scheduleAtFixedRate(() -> System.out.println("固定頻率"), 1, 2, TimeUnit.SECONDS);
// 3. 固定延遲:上次執行結束後延遲 2s 再執行
scheduler.scheduleWithFixedDelay(() -> System.out.println("固定延遲"), 1, 2, TimeUnit.SECONDS);
// 關閉
scheduler.shutdown();
| 方法 | 場景 | 特點 |
|---|---|---|
| schedule(Runnable, delay, unit) | 一次性延遲 | 返回 ScheduledFuture,可取消 |
| scheduleAtFixedRate(task, initialDelay, period, unit) | 固定頻率 | 嚴格時間間隔,任務執行時間長會並發執行 |
| scheduleWithFixedDelay(task, initialDelay, delay, unit) | 固定延遲 | 上次結束後延遲,適合耗時不固定任務 |
ScheduledThreadPoolExecutor,標準、健壯、支援取消、異常處理。DelayQueue + 自定義消費線程池。wait/notify 或更高層 Condition、CountDownLatch、CompletableFuture。