Spring Boot 內建 @Scheduled 提供輕量級定時任務支援,無需引入 Quartz 即可滿足大多數場景。本文系統梳理配置方式、執行模式、並行與異步進階技巧。
| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| java.util.Timer | JDK 內建、零依賴 | 單執行緒、無彈性排程、異常終止影響後續 | 簡單週期性任務 |
| Quartz | 功能強大、支援叢集、持久化、Cron 表達式 | 配置複雜、學習曲線陡 | 企業級複雜排程 |
| Spring @Scheduled | 輕量、整合 Spring 生態、注解驅動、支援 Cron/固定頻率 | 無持久化、叢集需額外配置 | 微服務、中小型專案首選 |
@SpringBootApplication
@EnableScheduling // 關鍵:掃描 @Scheduled 註解
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@Component
public class ScheduledTasks {
private static final Logger log = LoggerFactory.getLogger(ScheduledTasks.class);
// 每 2 分鐘執行一次
@Scheduled(cron = "0 0/2 * * * ?")
public void executeTask() {
Thread current = Thread.currentThread();
log.info("定時任務執行 - Thread ID: {}, Name: {}", current.getId(), current.getName());
// 業務邏輯...
}
// 固定頻率:上次開始後 5 秒執行
@Scheduled(fixedRate = 5000)
public void fixedRateTask() { /* ... */ }
// 固定延遲:上次結束後 3 秒執行
@Scheduled(fixedDelay = 3000)
public void fixedDelayTask() { /* ... */ }
// 初始延遲 10 秒,之後固定頻率
@Scheduled(initialDelay = 10000, fixedRate = 5000)
public void delayedTask() { /* ... */ }
}
┌───────────── 秒 (0-59)
│ ┌───────────── 分 (0-59)
│ │ ┌───────────── 時 (0-23)
│ │ │ ┌───────────── 日 (1-31)
│ │ │ │ ┌───────────── 月 (1-12)
│ │ │ │ │ ┌───────────── 星期 (0-7) (0或7=星期日)
│ │ │ │ │ │
* * * * * *
常用範例:
0 0 2 * * ?:每天凌晨 2 點0 0/5 * * * ?:每 5 分鐘0 0 0 * * MON:每週一午夜0 0 0 1 * ?:每月 1 號午夜Spring Boot 預設使用單一 TaskScheduler,所有任務共用一個執行緒,按提交順序串行執行。長時間任務會阻塞後續任務。
@Configuration
@EnableScheduling
public class ScheduleConfig implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(taskExecutor());
}
@Bean(destroyMethod = "shutdown")
public Executor taskExecutor() {
return Executors.newScheduledThreadPool(10); // 10 條執行緒池
}
}
@Configuration
@EnableScheduling
@EnableAsync // 啟用異步支援
public class AsyncScheduleConfig implements SchedulingConfigurer, AsyncConfigurer {
@Bean
public ThreadPoolTaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(20);
scheduler.setThreadNamePrefix("scheduled-task-");
scheduler.setAwaitTerminationSeconds(60);
scheduler.setWaitForTasksToCompleteOnShutdown(true);
return scheduler;
}
@Override
public Executor getAsyncExecutor() {
return taskScheduler(); // 異步任務同用此執行緒池
}
@Override
public void configureTasks(ScheduledTaskRegistrar registrar) {
registrar.setTaskScheduler(taskScheduler());
}
}
@SpringBootApplication
public class Application {
public static void main(String[] args) throws Exception {
// 標準啟動
SpringApplication.run(Application.class, args);
// 若使用 AnnotationConfigApplicationContext 手動裝配
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(AsyncScheduleConfig.class);
context.refresh();
}
}
@Scheduled(cron = "0 0 2 * * ?")
public void dailyReport() {
String lockKey = "dailyReport:" + LocalDate.now();
if (redisTemplate.opsForValue().setIfAbsent(lockKey, "1", Duration.ofHours(24))) {
try {
// 生成報表邏輯
} finally {
redisTemplate.delete(lockKey);
}
}
}
@Component
public class MonitoredTasks {
@Scheduled(cron = "0 0/10 * * * ?")
public void criticalTask() {
try {
// 核心業務
metrics.increment("task.success");
} catch (Exception e) {
metrics.increment("task.failure");
log.error("關鍵任務失敗", e);
alertService.sendAlert("定時任務異常: " + e.getMessage());
}
}
}
@Component
public class DynamicScheduledTask implements SchedulingConfigurer {
@Value("${task.cron:0 0 2 * * ?}")
private String cronExpression;
@Override
public void configureTasks(ScheduledTaskRegistrar registrar) {
registrar.addTriggerTask(
() -> executeBusinessLogic(),
triggerContext -> {
CronTrigger trigger = new CronTrigger(cronExpression);
return trigger.nextExecutionTime(triggerContext);
}
);
}
// 配合 @RefreshScope 實現熱更新
public void updateCron(String newCron) {
this.cronExpression = newCron;
}
}
| 問題 | 原因 | 解決 |
|---|---|---|
| 任務不執行 | 缺少 @EnableScheduling 或元件未被掃描 | 檢查主啟動類註解、@Component 掃描路徑 |
| 任務卡住/延遲 | 預設單執行緒阻塞 | 配置執行緒池並行執行 |
| 叢集重複執行 | 多實例同時啟動 | 分布式鎖(Redis/ZK)或 Quartz 叢集模式 |
| Cron 表達式錯誤 | 秒欄位位置、星期表示法差異 | 使用線上 Cron 生成器驗證 |
| 應用關閉時任務未完成 | 缺乏優雅關閉配置 | setWaitForTasksToCompleteOnShutdown(true) |
Spring Boot @Scheduled 以輕量級姿態解決 80% 定時任務需求。掌握「啟用 → 定義 → 並行優化 → 監控治理」完整鏈路,即可構建穩健可維運的排程系統。複雜場景(持久化、叢集、動態排程)可平滑遷移至 Quartz 或 XXL-JOB。