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

Java 定時任務實現指南:TimerTask 與 ServletContextListener 完整實踐

     編輯:Java 相關知識

前言

在 Java Web 應用中,定時自動執行任務(如定時備份、報表生成、資料同步)是常見需求。本文通過一個完整的檔案上傳範例,詳細講解如何利用 TimerTask 和 ServletContextListener 實現可靠的定時任務機制。

核心架構

整個方案包含四個核心組件:

  1. 業務 Bean:封裝具體執行邏輯(如檔案上傳)
  2. 定時任務類:繼承 TimerTask,定義執行條件與頻率
  3. 上下文監聽器:實現 ServletContextListener,管理任務生命週期
  4. Web.xml 配置:註冊監聽器,啟動時自動初始化

步驟一:建立業務執行類

package com.sun.time2;

public class UploadFile {
    /**
     * 實現上傳的具體邏輯
     * 參考:smarupload 實現、Struts2 多檔案上傳
     */
    public void upload() {
        System.out.println("系統自動執行檔案上傳任務");
        // 實際業務邏輯:連接 FTP、雲存儲、資料庫等
    }
}

步驟二:建立定時任務類

package com.sun.time2;

import java.util.Calendar;
import java.util.TimerTask;
import javax.servlet.ServletContext;

public class MyTask extends TimerTask {
    private static boolean isRunning = false;
    private final ServletContext context;
    private final Calendar calendar = Calendar.getInstance();

    public MyTask(ServletContext context) {
        this.context = context;
    }

    @Override
    public void run() {
        // 防止並發執行
        if (!isRunning) {
            // 每小時第 19 分鐘執行(可改為 Cron 表達式邏輯)
            if (19 == calendar.get(Calendar.MINUTE)) {
                isRunning = true;
                context.log("正在執行排程任務");
                
                try {
                    new UploadFile().upload();
                    context.log("任務順利完成");
                } catch (Exception e) {
                    context.log("任務執行失敗: " + e.getMessage());
                } finally {
                    isRunning = false;
                }
            }
        } else {
            System.out.println("上一任務尚未執行完畢,跳過本次");
        }
    }
}

步驟三:建立 Servlet 上下文監聽器

package com.sun.time2;

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

public class ContextListener implements ServletContextListener {
    private Timer timer = null;

    @Override
    public void contextInitialized(ServletContextEvent event) {
        timer = new Timer(true); // 守護線程,不阻塞 JVM 關閉
        event.getServletContext().log("伺服器已啟動,初始化排程任務");
        
        // 每 90 秒掃描一次(生產環境建議調整為 60 秒或更長)
        timer.schedule(new MyTask(event.getServletContext()), 0, 90_000);
        event.getServletContext().log("排程任務列表已載入");
    }

    @Override
    public void contextDestroyed(ServletContextEvent event) {
        if (timer != null) {
            timer.cancel();
            event.getServletContext().log("排程任務已銷毀");
        }
    }
}

步驟四:Web.xml 註冊監聽器

<listener>
    <listener-class>com.sun.time2.ContextListener</listener-class>
</listener>

進階優化建議

1. 使用 Spring @Scheduled 或 Quartz 替代

上述方案雖然可行,但存在硬編碼時間、單機故障無法切換、無法動態調整等問題。現代專案建議:

  • Spring Boot:@EnableScheduling + @Scheduled(cron = "0 19 * * * ?")
  • Quartz:支援叢集、持久化、Cron 表達式、動態任務管理
  • XXL-JOB:分散式任務調度平台,提供可視化管理

2. 關鍵改進點

問題原方案改進方案
時間硬編碼Calendar.MINUTE == 19外部化配置 + Cron 表達式
併發控制boolean 旗標分散式鎖
單點故障無叢集模式 + 領導者選舉
監控告警System.out/logMetrics + 告警系統

總結

基於 TimerTask + ServletContextListener 的定時任務實現簡單直接,適合輕量級單機應用。對於企業級專案,強烈建議採用成熟的調度框架(Quartz、Spring Scheduling、XXL-JOB),以獲得更好的可靠性、可觀測性和運維體驗。

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