Java 5 引入 ScheduledExecutorService 取代 Timer,提供執行緒池支援、彈性排程、例外處理等企業級功能。本文詳細說明核心 API、四種排程模式、關閉策略與生產環境最佳實務。
// 單執行緒排程器(適合輕量任務)
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
// 多執行緒排程器(指定核心執行緒數)
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(4);
// 自訂 ThreadFactory(命名、優先權、Daemon 等)
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(4, r -> {
Thread t = new Thread(r, "scheduler-worker-" + counter.incrementAndGet());
t.setDaemon(true);
return t;
});
| 方法 | 行為 | 適用場景 |
|---|---|---|
schedule(task, delay, unit) |
延遲 delay 後執行一次 |
啟動延遲初始化、一次性通知 |
scheduleAtFixedRate(task, initialDelay, period, unit) |
固定頻率執行(期間含任務執行時間) | 心跳檢測、定期同步、輪詢 |
scheduleWithFixedDelay(task, initialDelay, delay, unit) |
固定延遲執行(上次結束到下次開始) | 資源密集型任務、避免重疊 |
schedule(Callable, delay, unit) |
延遲執行並回傳 Future<V> |
需取得結果的異步計算 |
public class BeeperControl {
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
public void beepForOneMinute() {
Runnable beeper = () -> System.out.println("Beep at " + Instant.now());
// 每 10 秒執行一次,初始延遲 2 秒
ScheduledFuture<?> beeperHandle = scheduler.scheduleAtFixedRate(beeper, 2, 10, TimeUnit.SECONDS);
// 60 秒後取消任務並關閉排程器
scheduler.schedule(() -> {
beeperHandle.cancel(false); // 不中斷正在執行的任務
scheduler.shutdown(); // 拒絕新任務,等待現有任務完成
System.out.println("排程器已關閉");
}, 60, TimeUnit.SECONDS);
}
public static void main(String[] args) {
new BeeperControl().beepForOneMinute();
}
}
// 假設任務執行 3 秒,period/delay 設 5 秒
// scheduleAtFixedRate: T=0啟動 → T=3結束 → T=5啟動下一次 (間隔 2 秒等待)
// scheduleWithFixedDelay: T=0啟動 → T=3結束 → T=8啟動下一次 (固定等待 5 秒)
選擇原則:任務執行時間不可預測或嚴禁重疊 → FixedDelay;需嚴格時間間隔(如每整點) → FixedRate。
public void shutdownGracefully(ScheduledExecutorService scheduler, long timeout, TimeUnit unit) {
scheduler.shutdown(); // 1. 拒絕新任務
try {
if (!scheduler.awaitTermination(timeout, unit)) {
// 2. 強制關閉未完成任務
List<Runnable> remaining = scheduler.shutdownNow();
System.out.println("強制關閉,剩餘任務: " + remaining.size());
// 3. 再次等待執行緒終止
if (!scheduler.awaitTermination(timeout, unit)) {
System.err.println("排程器未能正常終止");
}
}
} catch (InterruptedException ex) {
scheduler.shutdownNow();
Thread.currentThread().interrupt(); // 保留中斷狀態
}
}
newScheduledThreadPool 使用無界隊列,可能導致 OOM。自訂 ThreadPoolExecutor 並設定有界隊列與拒絕策略。run() 內完整捕獲。queue.size、active.threads、task.completed。@Scheduled(cron="0 0 2 * * ?"),原生 API 僅支援固定間隔。import java.time.Instant;
import java.util.concurrent.*;
public class SchedulerDemo {
public static void main(String[] args) throws InterruptedException {
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2,
new ThreadFactoryBuilder().setNameFormat("demo-scheduler-%d").build());
// 1. 一次性延遲任務
scheduler.schedule(() -> System.out.println("一次性任務: " + Instant.now()),
1, TimeUnit.SECONDS);
// 2. 固定頻率任務(每 2 秒)
ScheduledFuture<?> fixedRate = scheduler.scheduleAtFixedRate(() ->
System.out.println("FixedRate: " + Instant.now()), 2, 2, TimeUnit.SECONDS);
// 3. 固定延遲任務(結束後 3 秒再跑)
ScheduledFuture<?> fixedDelay = scheduler.scheduleWithFixedDelay(() -> {
try { Thread.sleep(1000); } catch (InterruptedException ignored) {}
System.out.println("FixedDelay: " + Instant.now());
}, 1, 3, TimeUnit.SECONDS);
// 10 秒後取消所有並關閉
Thread.sleep(10_000);
fixedRate.cancel(false);
fixedDelay.cancel(false);
shutdownGracefully(scheduler, 5, TimeUnit.SECONDS);
}
// 參考上方 shutdownGracefully 方法
}
ScheduledExecutorService 是 Java 標準庫中功能最完整的排程工具。掌握四種排程模式差異、正確關閉流程、例外隔離與生產環境調優要點,即可構建穩定可靠的定時任務系統。複雜 Cron 需求建議引入 Quartz 或 Spring Scheduling。