在 Web 容器中實現定時排程,除引入 Quartz 等重型框架外,Java 標準庫 java.util.Timer 與 TimerTask 配合 ServletContextListener 即可構建零依賴的輕量級方案。本文提供完整三步驟實作範本。
ServletContextListener,容器啟動時初始化 Timer、註冊排程;關閉時銷毀 Timer。TimerTask,實作 run() 業務邏輯,含併發保護機制。public class TaskManager implements ServletContextListener {
private static final long PERIOD_DAY = 24 * 60 * 60 * 1000L;
private static final long PERIOD_WEEK = PERIOD_DAY * 7;
private static final long NO_DELAY = 0L;
private Timer timer;
@Override
public void contextInitialized(ServletContextEvent sce) {
timer = new Timer("DB-Backup-Timer", true); // Daemon 執行緒
// 每 4 週(約一月)執行一次備份
timer.schedule(new BackUpTableTask(), NO_DELAY, PERIOD_WEEK * 4);
// 測試用:每 30 秒執行一次
// timer.schedule(new BackUpTableTask(), NO_DELAY, 30_000);
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
timer.cancel(); // 釋放 Timer 執行緒與排程
}
}
public class BackUpTableTask extends TimerTask {
private static final Log LOG = LogFactory.getLog(BackUpTableTask.class);
private static volatile boolean isRunning = false;
@Override
public void run() {
if (!isRunning) {
isRunning = true;
try {
LOG.debug("開始執行資料庫備份任務...");
// TODO: 實際備份邏輯(呼叫 Service/DAO)
// backupService.executeFullBackup();
LOG.debug("資料庫備份任務完成.");
} catch (Exception e) {
LOG.error("備份任務異常", e);
} finally {
isRunning = false;
}
} else {
LOG.warn("上一次備份任務尚未結束,跳過本次排程.");
}
}
}
<listener>
<listener-class>com.example.task.TaskManager</listener-class>
<description>資料庫定時備份任務</description>
</listener>
| 議題 | 處理策略 |
|---|---|
| 併發重入 | volatile boolean isRunning 雙重檢查,避免長任務重疊執行 |
| 例外隔離 | run() 內部 try-catch 吞併例外,防止單次失敗導致 Timer 執行緒終止 |
| Daemon 執行緒 | new Timer(name, true) 標記為守護執行緒,不阻塞 JVM 關閉 |
| 資源釋放 | contextDestroyed 呼叫 timer.cancel() 清理排程佇列 |
| 叢集環境 | 單機方案;叢集需引入 Quartz + JDBC JobStore 或分散式排程平台 |
善用標準庫解決簡單問題,是工程師務實取捨的體現。