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

Spring 定時任務進階配置:@Scheduled + TaskScheduler 註解驅動模式詳解

     編輯:Java 相關知識

從 XML 配置到註解驅動的演進

早期需在 XML 定義 MethodInvokingJobDetailFactoryBean + CronTriggerFactoryBean + SchedulerFactoryBean,繁瑣易錯。Spring 3.0+ 引入 @Scheduled + @EnableScheduling,大幅簡化。

Maven 依賴

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context-support</artifactId>
  <version>5.3.21</version>
</dependency>
<dependency>
  <groupId>org.quartz-scheduler</groupId>
  <artifactId>quartz</artifactId>
  <version>2.3.2</version>
</dependency>

Spring XML 啟用註解調度

<beans xmlns:task="https://www.springframework.org/schema/task"
       xsi:schemaLocation="https://www.springframework.org/schema/task
       https://www.springframework.org/schema/task/spring-task.xsd">

  <!-- 線程池調度器 -->
  <task:scheduler id="taskScheduler" pool-size="10"/>
  
  <!-- 啟用 @Scheduled 掃描 -->
  <task:annotation-driven scheduler="taskScheduler"/>
</beans>

Java Config 等效配置

@Configuration
@EnableScheduling
public class ScheduleConfig implements SchedulingConfigurer {
    @Override
    public void configureTasks(ScheduledTaskRegistrar registrar) {
        registrar.setTaskScheduler(new ThreadPoolTaskScheduler() {{
            setPoolSize(10);
            setThreadNamePrefix("scheduled-");
            initialize();
        }});
    }
}

業務任務實現

@Component
public class OrderTimeoutTask {
    private static final DateTimeFormatter FMT = 
        DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    @Scheduled(cron = "0/10 * * * * ?") // 每 10 秒
    public void checkTimeoutOrders() {
        try {
            TimeUnit.SECONDS.sleep(20); // 模擬耗時邏輯
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        log.info("{} ********* 定時任務執行", LocalDateTime.now().format(FMT));
    }
}

Cron 表達式詳解

秒 分 時 日 月 周 [年]
*  *  *  *  *  ?      每秒
0  0  12 *  *  ?      每天中午 12:00
0  15 10 ?  *  MON-FRI 工作日 10:15
0  0  0  1  *  ?      每月 1 號 00:00
0  0  0  ?  *  6#3    每月第三個周六

並發執行風險與解決

現象:任務執行時間 > 間隔時間 → 線程池佔滿、數據重複處理

  • @Scheduled 默認單線程串行(同一調度器),長任務會阻塞後續觸發
  • 解決方案:
    1. 增加 pool-size 並配置 @Async 異步執行
    2. 使用分布式鎖(Redis/DB)保證單實例執行
    3. 任務內部檢查上次執行狀態,跳過重疊

動態定時任務(運行時增删改)

@Component
public class DynamicTaskManager {
    @Resource private ThreadPoolTaskScheduler scheduler;
    private final Map> futures = new ConcurrentHashMap<>();

    public void addTask(String name, Runnable task, String cron) {
        CronTrigger trigger = new CronTrigger(cron);
        ScheduledFuture future = scheduler.schedule(task, trigger);
        futures.put(name, future);
    }

    public void removeTask(String name) {
        ScheduledFuture f = futures.remove(name);
        if (f != null) f.cancel(true);
    }
}

監控與排查

  • Actuator 端點:/actuator/scheduledtasks 查看下次執行時間、執行統計
  • 日誌追蹤:設置 logging.level.org.springframework.scheduling=DEBUG
  • 常見坑:cron="0/10 * * * * ?" 缺少秒位會報錯,Spring Cron 6 位必須以秒開頭
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved