Timer 核心類別結構
java.util.Timer:排程器,維護任務隊列,單一背景執行緒循環執行
java.util.TimerTask:抽象類別,實現 Runnable,需重寫 run() 定義任務邏輯
四種排程模式完整解析
| 方法簽名 | 行為 | 適用場景 |
schedule(TimerTask task, long delay) | 延遲 delay 毫秒後執行一次 | 一次性延遲任務(如啟動後初始化) |
schedule(TimerTask task, Date time) | 在指定時間 time 執行一次 | 固定時間點觸發(如每日零點備份) |
schedule(TimerTask task, long delay, long period) | 延遲 delay 後,每 period 重複執行 | 固定頻率重複任務(如每分鐘心跳檢測) |
schedule(TimerTask task, Date firstTime, long period) | 在 firstTime 首次執行,之後每 period 重複 | 指定啟動時間的週期性任務 |
scheduleAtFixedRate(...) | 固定頻率執行,若任務延遲會追趕執行 | 時間敏感型週期任務(如每秒採樣) |
實戰程式碼範例
基礎延遲任務
public class DelayedTaskDemo {
public static void main(String[] args) {
Timer timer = new Timer();
// 3 秒後執行一次
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("延遲任務執行: " + new Date());
}
}, 3000);
// 主執行緒等待,否則 JVM 可能退出
try { Thread.sleep(5000); } catch (InterruptedException e) {}
timer.cancel(); // 釋放資源
}
}
週期性任務
public class PeriodicTaskDemo {
public static void main(String[] args) {
Timer timer = new Timer();
// 立即啟動,每 2 秒執行一次
timer.scheduleAtFixedRate(new TimerTask() {
int count = 0;
@Override
public void run() {
System.out.printf("週期任務 #%d: %s%n", ++count, new Date());
if (count >= 5) {
timer.cancel(); // 任務內部取消排程器
}
}
}, 0, 2000);
}
}
指定時間執行
public class FixedTimeTaskDemo {
public static void main(String[] args) {
Timer timer = new Timer();
// 計算下一個整點時間
Calendar cal = Calendar.getInstance();
cal.add(Calendar.HOUR, 1);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
Date nextHour = cal.getTime();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("整點報時: " + new Date());
}
}, nextHour, 3600_000); // 之後每小時執行
}
}
Timer 關鍵特性與限制
| 特性 | 說明 |
| 單執行緒模型 | 所有任務共用一個背景執行緒,長時間任務會阻塞後續任務 |
| 異常處理 | 任務拋出未捕獲異常會導致 Timer 執行緒終止,所有排程任務失效 |
| 時間漂移 | schedule 固定延遲,累積誤差;scheduleAtFixedRate 固定頻率,會追趕 |
| 非持久化 | JVM 重啟後排程丟失,無法應對叢集環境 |
| 無事務支援 | 任務執行不在事務上下文中 |
企業級應用:Web 專案整合
ServletContextListener 啟動排程
@WebListener
public class SchedulerInitializer implements ServletContextListener {
private Timer timer;
@Override
public void contextInitialized(ServletContextEvent sce) {
timer = new Timer(true); // daemon 執行緒,不阻止 JVM 關閉
// 每日清理過期 Session
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
try {
SessionCleaner.cleanExpiredSessions();
} catch (Exception e) {
// 必須捕獲異常,防止 Timer 執行緒死亡
log.error("Session 清理失敗", e);
}
}
}, calculateFirstMidnight(), 24 * 3600_000);
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
timer.cancel(); // 優雅關閉
}
private Date calculateFirstMidnight() { /* 計算下一個零點 */ }
}
現代替代方案對比
| 方案 | 優點 | 缺點 | 推薦場景 |
| Timer | 輕量、標準庫、零依賴 | 單執行緒、無持久化、異常脆弱 | 簡單獨立應用、單機定時 |
| ScheduledExecutorService | 執行緒池、異常隔離、靈活關閉 | 無持久化、無叢集支援 | Spring 以前的標準替代 |
| Spring @Scheduled | 聲明式、支援 Cron、整合事務 | 依賴 Spring、單機模式 | Spring/Spring Boot 專案首選 |
| Quartz | 持久化、叢集、Cron、監控 | 配置複雜、重量級 | 企業級、叢集、高可靠需求 |
| XXL-Job | 分散式、Web 控制台、分片、故障轉移 | 需部署調度中心 | 微服務架構、跨語言、運維友好 |
Spring Boot 整合範例
@Configuration
@EnableScheduling
public class SchedulerConfig implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar registrar) {
registrar.setScheduler(Executors.newScheduledThreadPool(10));
}
}
@Component
public class BusinessScheduler {
// 每日 02:00 執行
@Scheduled(cron = "0 0 2 * * ?")
public void dailyReport() {
reportService.generateDailyReport();
}
// 固定頻率 5 分鐘
@Scheduled(fixedRate = 5 * 60 * 1000)
public void healthCheck() {
monitorService.checkSystemHealth();
}
}
最佳實踐總結
- 任務邏輯務必 try-catch:防止單一任務異常導致整個 Timer 失效
- 使用 daemon 執行緒:
new Timer(true),不阻塞 JVM 關閉
- 避免長時間任務:耗時操作提交到執行緒池異步處理
- 優雅關閉:
timer.cancel() + timer.purge() 清除已取消任務
- 生產環境首選 Spring @Scheduled 或 Quartz:功能完善、社群活躍
- 上一篇文章: Java 封裝核心:Getter/Setter 方法深度解析與實戰應用
- 下一篇文章: HashMap 深度解析:核心原理、Put/Get 流程、哈希碼機制與最佳實踐
Copyright ©
程式碼之家 All Rights Reserved