首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 程式設計入門

Java Timer 與 TimerTask 實戰:從基礎煮蛋計時器到通用排程框架

     編輯:Java 程式設計入門

標準計時器框架回顧

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) 等重複排程方法。

標準框架的侷限性

  • 無法表達「每天 02:00 執行」——夏令時切換會導致時間跳躍/重疊
  • 無法表達「每月最後一天」、「每個工作日」
  • 異常處理:TimerTask.run() 拋出未捕獲異常會導致整個 Timer 線程終止,後續任務全停
  • 單線程序列執行:長任務阻塞後續任務

構建通用排程框架(核心介面與兩類)

核心介面:Trigger(觸發條件)

public interface Trigger {
    Date nextExecutionTime(Date currentTime); // 給定當前時間,返回下次執行時間
}

實現範例:DailyTrigger

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();
    }
}

排程器:Scheduler

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 表達式)
  • 持久化排程:資料庫儲存任務狀態,支援叢集、重啟恢復
  • 線程池:替換單線程 Timer,使用 ScheduledExecutorService(JDK 5+)實現並行執行
  • 監控與管理:JMX 公開任務狀態、動態增刪改查

現代替代方案

框架特點
Spring Task / @Scheduled聲明式、支援 Cron、固定延遲/頻率、整合 Spring 生命週期
Quartz企業級、持久化、叢集、Cron、監聽器、JMX
ScheduledExecutorServiceJDK 內建、線程池、無 Cron 支援

總結

Timer/TimerTask 適合簡單延遲/週期任務。業務級排程需求(日曆邏輯、異常隔離、叢集高可用)請直接採用成熟框架。理解Trigger 抽象與遞歸排程模式,有助於閱讀框架源碼與設計客製化排程系統。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved