java.util.Timer + TimerTask(JDK 1.3 引入)解決了早期自寫排程器的線程與 Object.wait() 複雜性,但缺乏彈性:無法直接表達「每天固定時間」、「工作日」、「夏令時調整」等業務排程需求。
public class EggTimer {
private final Timer timer = new Timer();
private final int minutes;
public EggTimer(int minutes) { this.minutes = minutes; }
public void start() {
timer.schedule(new TimerTask() {
public void run() {
playSound();
timer.cancel(); // 單次任務結束關閉計時器
}
private void playSound() { System.out.println("Your egg is ready!"); }
}, minutes * 60 * 1000L); // 延遲毫秒數
}
public static void main(String[] args) { new EggTimer(2).start(); }
}
schedule(TimerTask, delay):單次延遲執行。另有 scheduleAtFixedRate、schedule(TimerTask, firstTime, period) 等重複排程方法。
TimerTask.run() 拋出未捕獲異常會導致整個 Timer 線程終止,後續任務全停public interface Trigger {
Date nextExecutionTime(Date currentTime); // 給定當前時間,返回下次執行時間
}
public class DailyTrigger implements Trigger {
private final int hour, minute;
public DailyTrigger(int hour, int minute) { this.hour = hour; this.minute = minute; }
public Date nextExecutionTime(Date currentTime) {
Calendar cal = Calendar.getInstance();
cal.setTime(currentTime);
cal.set(Calendar.HOUR_OF_DAY, hour);
cal.set(Calendar.MINUTE, minute);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
if (!cal.getTime().after(currentTime)) cal.add(Calendar.DAY, 1);
return cal.getTime();
}
}
public class Scheduler {
private final Timer timer = new Timer(true); // daemon 線程
public void schedule(Runnable task, Trigger trigger) {
timer.schedule(new TimerTask() {
public void run() {
try { task.run(); }
catch (Throwable t) { logError(t); } // 隔離異常,不影響後續任務
scheduleNext(task, trigger); // 遞歸排程下一次
}
}, trigger.nextExecutionTime(new Date()));
}
private void scheduleNext(Runnable task, Trigger trigger) {
timer.schedule(new TimerTask() {
public void run() {
try { task.run(); } catch (Throwable t) { logError(t); }
scheduleNext(task, trigger);
}
}, trigger.nextExecutionTime(new Date()));
}
private void logError(Throwable t) { /* 記錄日誌 */ }
}
Scheduler scheduler = new Scheduler();
// 每天 02:30 備份
scheduler.schedule(() -> backupDatabase(), new DailyTrigger(2, 30));
// 每小時整點檢查
scheduler.schedule(() -> healthCheck(), new Trigger() {
public Date nextExecutionTime(Date now) {
Calendar c = Calendar.getInstance();
c.setTime(now);
c.add(Calendar.HOUR, 1);
c.set(Calendar.MINUTE, 0); c.set(Calendar.SECOND, 0); c.set(Calendar.MILLISECOND, 0);
return c.getTime();
}
});
AndTrigger、OrTrigger、CronTrigger(解析 Cron 表達式)ScheduledExecutorService(JDK 5+)實現並行執行| 框架 | 特點 |
|---|---|
| Spring Task / @Scheduled | 聲明式、支援 Cron、固定延遲/頻率、整合 Spring 生命週期 |
| Quartz | 企業級、持久化、叢集、Cron、監聽器、JMX |
| ScheduledExecutorService | JDK 內建、線程池、無 Cron 支援 |
Timer/TimerTask 適合簡單延遲/週期任務。業務級排程需求(日曆邏輯、異常隔離、叢集高可用)請直接採用成熟框架。理解Trigger 抽象與遞歸排程模式,有助於閱讀框架源碼與設計客製化排程系統。