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

Web 應用實作排程任務:基於 ServletContextListener 與 Timer 的輕量方案

     編輯:Java 相關知識

背景

Web 應用常需定期執行維護任務(資料備份、報表產生、快取刷新)。除引入 Quartz 等重量級框架,JDK 內建 java.util.Timer 與 TimerTask 配合 ServletContextListener 即可實現輕量級排程,無額外依賴。

三步驟實作

步驟 1:任務管理器 — 實作 ServletContextListener

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import java.util.Timer;

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 = 0;

    private Timer timer;

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        timer = new 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(); // 關閉排程器,釋放執行緒
    }
}

步驟 2:具體任務類別 — 繼承 TimerTask

import java.util.TimerTask;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class BackUpTableTask extends TimerTask {

    private static final Log log = LogFactory.getLog(BackUpTableTask.class);
    private static boolean isRunning = false;

    @Override
    public void run() {
        if (!isRunning) {
            isRunning = true;
            try {
                log.debug("開始執行備份任務...");
                // TODO: 實作實際備份邏輯
                // backupDatabase();
                log.debug("備份任務完成...");
            } catch (Exception e) {
                log.error("備份任務異常", e);
            } finally {
                isRunning = false;
            }
        } else {
            log.warn("上一輪備份尚未結束,跳過本次執行");
        }
    }
}

關鍵點:isRunning 旗標防止任務重疊執行;實際專案建議改用 AtomicBoolean 或 ReentrantLock 確保執行緒安全。

步驟 3:web.xml 註冊監聽器

<listener>
    <listener-class>com.example.task.TaskManager</listener-class>
    <description>資料庫定期備份排程任務</description>
</listener>

進階考量

  • 叢集環境:多節點部署時,每個節點都會啟動排程,導致重複執行。解法:引入分散式鎖(資料庫唯一鍵、ZooKeeper、Redis SETNX)或改用 Quartz 叢集模式。
  • 持久化排程:Timer 重啟後失去狀態,無法支援「錯過執行時間自動補跑」。Quartz 持久化 JobStore 可解決。
  • 例外處理:run() 內未捕獲例外會導致 Timer 執行緒終結,後續排程失效。務必在 run() 完整捕獲並記錄例外。
  • 現代替代方案:Spring @Scheduled、@EnableScheduling 提供聲明式排程,支援 Cron 運算式、固定延遲/頻率,整合 Spring 容器生命週期,推薦 Spring 專案優先採用。

總結

Timer + ServletContextListener 適合簡單、單節點、無持久化需求的輕量排程。隨著系統複雜度提升,建議遷移至 Quartz 或 Spring Scheduling 獲得企業級功能。

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