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

ScheduledExecutorService 定時任務排程完整指南

     編輯:Java 相關知識

前言

Java 5 引入 ScheduledExecutorService 取代 Timer,提供執行緒池支援、彈性排程、例外處理等企業級功能。本文詳細說明核心 API、四種排程模式、關閉策略與生產環境最佳實務。

核心介面與建立方式

// 單執行緒排程器(適合輕量任務)
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();

// 多執行緒排程器(指定核心執行緒數)
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(4);

// 自訂 ThreadFactory(命名、優先權、Daemon 等)
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(4, r -> {
    Thread t = new Thread(r, "scheduler-worker-" + counter.incrementAndGet());
    t.setDaemon(true);
    return t;
});

四種排程模式完整對照

方法行為適用場景
schedule(task, delay, unit) 延遲 delay 後執行一次 啟動延遲初始化、一次性通知
scheduleAtFixedRate(task, initialDelay, period, unit) 固定頻率執行(期間含任務執行時間) 心跳檢測、定期同步、輪詢
scheduleWithFixedDelay(task, initialDelay, delay, unit) 固定延遲執行(上次結束到下次開始) 資源密集型任務、避免重疊
schedule(Callable, delay, unit) 延遲執行並回傳 Future<V> 需取得結果的異步計算

實戰範例:每 10 秒啟動,運行 1 分鐘後自動關閉

public class BeeperControl {
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

    public void beepForOneMinute() {
        Runnable beeper = () -> System.out.println("Beep at " + Instant.now());
        
        // 每 10 秒執行一次,初始延遲 2 秒
        ScheduledFuture<?> beeperHandle = scheduler.scheduleAtFixedRate(beeper, 2, 10, TimeUnit.SECONDS);
        
        // 60 秒後取消任務並關閉排程器
        scheduler.schedule(() -> {
            beeperHandle.cancel(false); // 不中斷正在執行的任務
            scheduler.shutdown();       // 拒絕新任務,等待現有任務完成
            System.out.println("排程器已關閉");
        }, 60, TimeUnit.SECONDS);
    }

    public static void main(String[] args) {
        new BeeperControl().beepForOneMinute();
    }
}

關鍵細節:FixedRate vs FixedDelay


// 假設任務執行 3 秒,period/delay 設 5 秒
// scheduleAtFixedRate: T=0啟動 → T=3結束 → T=5啟動下一次 (間隔 2 秒等待)
// scheduleWithFixedDelay: T=0啟動 → T=3結束 → T=8啟動下一次 (固定等待 5 秒)

選擇原則:任務執行時間不可預測或嚴禁重疊 → FixedDelay;需嚴格時間間隔(如每整點) → FixedRate。

優雅關閉與例外處理

public void shutdownGracefully(ScheduledExecutorService scheduler, long timeout, TimeUnit unit) {
    scheduler.shutdown(); // 1. 拒絕新任務
    try {
        if (!scheduler.awaitTermination(timeout, unit)) {
            // 2. 強制關閉未完成任務
            List<Runnable> remaining = scheduler.shutdownNow();
            System.out.println("強制關閉,剩餘任務: " + remaining.size());
            // 3. 再次等待執行緒終止
            if (!scheduler.awaitTermination(timeout, unit)) {
                System.err.println("排程器未能正常終止");
            }
        }
    } catch (InterruptedException ex) {
        scheduler.shutdownNow();
        Thread.currentThread().interrupt(); // 保留中斷狀態
    }
}

生產環境最佳實務

  • 避免使用 Executors 預設工廠:newScheduledThreadPool 使用無界隊列,可能導致 OOM。自訂 ThreadPoolExecutor 並設定有界隊列與拒絕策略。
  • 任務包裝 Try-Catch:排程任務拋出未捕獲異常會導致該執行緒終止,後續排程不再執行。務必在 run() 內完整捕獲。
  • 監控指標:整合 Micrometer/Prometheus 監控 queue.size、active.threads、task.completed。
  • 時區與夏令時間:跨天/跨月排程建議使用 Quartz 或 Spring @Scheduled(cron="0 0 2 * * ?"),原生 API 僅支援固定間隔。
  • 叢集環境:多節點部署時需分布式鎖(Redis/Zookeeper)或 Quartz JDBC JobStore 防止重複執行。

完整可執行範例

import java.time.Instant;
import java.util.concurrent.*;

public class SchedulerDemo {
    public static void main(String[] args) throws InterruptedException {
        ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2, 
            new ThreadFactoryBuilder().setNameFormat("demo-scheduler-%d").build());

        // 1. 一次性延遲任務
        scheduler.schedule(() -> System.out.println("一次性任務: " + Instant.now()), 
            1, TimeUnit.SECONDS);

        // 2. 固定頻率任務(每 2 秒)
        ScheduledFuture<?> fixedRate = scheduler.scheduleAtFixedRate(() -> 
            System.out.println("FixedRate: " + Instant.now()), 2, 2, TimeUnit.SECONDS);

        // 3. 固定延遲任務(結束後 3 秒再跑)
        ScheduledFuture<?> fixedDelay = scheduler.scheduleWithFixedDelay(() -> {
            try { Thread.sleep(1000); } catch (InterruptedException ignored) {}
            System.out.println("FixedDelay: " + Instant.now());
        }, 1, 3, TimeUnit.SECONDS);

        // 10 秒後取消所有並關閉
        Thread.sleep(10_000);
        fixedRate.cancel(false);
        fixedDelay.cancel(false);
        shutdownGracefully(scheduler, 5, TimeUnit.SECONDS);
    }
    
    // 參考上方 shutdownGracefully 方法
}

總結

ScheduledExecutorService 是 Java 標準庫中功能最完整的排程工具。掌握四種排程模式差異、正確關閉流程、例外隔離與生產環境調優要點,即可構建穩定可靠的定時任務系統。複雜 Cron 需求建議引入 Quartz 或 Spring Scheduling。

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