【Springboot知识】Springboot计划任务Schedule详解

文章目录

Spring Boot 定时任务从原理到实现详解

一、核心原理分析

1. 架构分层

graph TD A[Application] --> B[@EnableScheduling] B --> C[ScheduledAnnotationBeanPostProcessor] C --> D[TaskScheduler] D --> E[ThreadPoolTaskScheduler] D --> F[ConcurrentTaskScheduler]

2. 核心组件

  • @EnableScheduling:启用定时任务自动配置
  • ScheduledAnnotationBeanPostProcessor:解析@Scheduled注解
  • TaskScheduler:任务调度接口
  • CronTrigger:处理cron表达式

3. 线程模型

java 复制代码
// 默认线程池配置
public class ThreadPoolTaskScheduler extends ExecutorConfigurationSupport
    implements TaskScheduler, SchedulingTaskExecutor {
    
    private volatile int poolSize = 1; // 默认单线程
    private ThreadFactory threadFactory = new CustomizableThreadFactory("task-scheduler-");
}

二、基础实现步骤

1. 添加依赖

xml 复制代码
<!-- pom.xml -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>

2. 主类配置

java 复制代码
@SpringBootApplication
@EnableScheduling
public class SchedulingApplication {
    public static void main(String[] args) {
        SpringApplication.run(SchedulingApplication.class, args);
    }
}

3. 定时任务类

java 复制代码
@Component
public class ScheduledTasks {
    private static final Logger log = LoggerFactory.getLogger(ScheduledTasks.class);

    // 固定频率(任务开始时间间隔)
    @Scheduled(fixedRate = 5000)
    public void fixedRateTask() {
        log.info("Fixed Rate Task :: Execution Time - {}", LocalDateTime.now());
    }

    // 固定延迟(任务结束时间间隔)
    @Scheduled(fixedDelay = 7000, initialDelay = 2000)
    public void fixedDelayTask() {
        log.info("Fixed Delay Task :: Execution Time - {}", LocalDateTime.now());
    }

    // Cron表达式
    @Scheduled(cron = "0 0/15 9-17 * * MON-FRI")
    public void cronTask() {
        log.info("Cron Task :: Execution Time - {}", LocalDateTime.now());
    }
}

三、高级配置技巧

1. 自定义线程池

java 复制代码
@Configuration
public class SchedulerConfig implements SchedulingConfigurer {
    
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setPoolSize(5);
        taskScheduler.setThreadNamePrefix("custom-scheduler-");
        taskScheduler.initialize();
        
        taskRegistrar.setTaskScheduler(taskScheduler);
    }
}

2. 动态配置参数

properties 复制代码
application.properties
schedule.rate=10000
schedule.delay=5000
schedule.cron=0 0 8 * * *
java 复制代码
@Scheduled(fixedRateString = "${schedule.rate}")
public void dynamicRateTask() {
    // ...
}

@Scheduled(cron = "${schedule.cron}")
public void dynamicCronTask() {
    // ...
}

3. 分布式锁集成(Redis示例)

java 复制代码
@Scheduled(fixedRate = 10000)
public void distributedTask() {
    String lockKey = "scheduledTaskLock";
    String requestId = UUID.randomUUID().toString();
    
    try {
        if (redisLockUtil.tryGetLock(lockKey, requestId, 30)) {
            log.info("Acquired lock, executing task...");
            // 业务逻辑
        }
    } finally {
        redisLockUtil.releaseLock(lockKey, requestId);
    }
}

四、异常处理机制

1. 统一异常处理器

java 复制代码
@ControllerAdvice
public class SchedulingExceptionHandler {
    
    @ExceptionHandler(TaskExecutionException.class)
    public void handleTaskException(TaskExecutionException ex) {
        log.error("Scheduled task failed: {}", ex.getMessage());
        // 发送告警通知
    }
}

2. 任务重试机制

java 复制代码
@Retryable(maxAttempts = 3, backoff = @Backoff(delay = 1000))
@Scheduled(fixedRate = 5000)
public void retryableTask() {
    // 可能失败的业务逻辑
    if (Math.random() > 0.5) {
        throw new RuntimeException("Simulated error");
    }
}

五、监控与调试

1. Actuator 端点

properties 复制代码
management.endpoints.web.exposure.include=scheduledtasks

访问 /actuator/scheduledtasks 查看任务列表:

json 复制代码
{
    "cron": [
        {
            "runnable": {
                "target": "com.example.ScheduledTasks.cronTask"
            },
            "expression": "0 0/15 9-17 * * MON-FRI"
        }
    ]
}

2. 性能监控

java 复制代码
@Scheduled(fixedRate = 5000)
@Timed(value = "scheduled.task", description = "监控任务执行时间")
public void monitoredTask() {
    // 业务逻辑
}

六、完整示例项目结构

复制代码
scheduling-demo/
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   └── com/example/
│   │   │       ├── config/
│   │   │       │   └── SchedulerConfig.java
│   │   │       ├── ScheduledTasks.java
│   │   │       └── SchedulingApplication.java
│   │   └── resources/
│   │       ├── application.properties
│   │       └── logback-spring.xml
│   └── test/
└── pom.xml

七、最佳实践建议

  1. 线程池配置原则

    properties 复制代码
    # 推荐线程数 = CPU核心数 * 2(IO密集型)
    # 推荐线程数 = CPU核心数 + 1(计算密集型)
  2. 任务设计规范

    • 单任务执行时间 < 调度间隔时间
    • 添加事务边界控制
    • 避免任务间状态共享
  3. 生产环境注意事项

    java 复制代码
    // 添加健康检查
    @Component
    public class ScheduleHealthIndicator implements HealthIndicator {
        
        @Override
        public Health health() {
            // 检查任务最后执行时间
            return Health.up().build();
        }
    }

通过以上配置和实现,可以构建出高可靠、易维护的定时任务系统。定时任务的执行频率需要根据实际业务需求进行合理设置,同时要特别注意在分布式环境下的任务协调问题。

Schedule注解参数详细说明

一、注解基础参数详解

1. 核心参数配置
java 复制代码
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(Schedules.class)
public @interface Scheduled {
    String cron() default "";
    String zone() default "";
    long fixedDelay() default -1;
    String fixedDelayString() default "";
    long fixedRate() default -1;
    String fixedRateString() default "";
    long initialDelay() default -1;
    String initialDelayString() default "";
}
2. 参数对照表
参数名称 类型 必填 默认值 说明
cron String "" Unix风格的cron表达式
zone String "" 时区ID(如"Asia/Shanghai")
fixedDelay long -1 上次执行结束到下次执行的间隔(毫秒)
fixedDelayString String "" 支持占位符的字符串形式(如"${schedule.delay}")
fixedRate long -1 固定频率执行(毫秒)
fixedRateString String "" 支持占位符的字符串形式
initialDelay long -1 首次执行的延迟时间(毫秒)
initialDelayString String "" 支持占位符的字符串形式

二、Cron表达式详解

1. 标准格式
复制代码
秒(0-59) 分(0-59) 时(0-23) 日(1-31) 月(1-12) 周(0-7) 年(可选)
2. 特殊字符说明
字符 含义 示例 说明
* 任意值 0 * * * * * 每分钟0秒执行
? 不指定(仅日/周字段) 0 0 0 ? * MON 每周一0点执行
- 范围 0 0 9-17 * * * 每天9点到17点整点执行
, 多个值 0 0 8,12,18 * * * 每天8、12、18点执行
/ 间隔频率 0 0/15 * * * * 每15分钟执行一次
L 最后一天/最后一周 0 0 0 L * ? 每月最后一天0点执行
W 最近工作日 0 0 0 LW * ? 每月最后一个工作日执行
# 第几个周几 0 0 0 ? * 5#2 每月第2个周四执行
3. 常用表达式示例
java 复制代码
@Scheduled(cron = "0 0 3 * * ?")      // 每天凌晨3点执行
@Scheduled(cron = "0 0/5 9-17 * * MON-FRI") // 工作日9-17点每5分钟执行
@Scheduled(cron = "0 0 12 1 * ?")    // 每月1号中午12点执行
@Scheduled(cron = "0 0 8 L * ?")      // 每月最后一天上午8点执行

三、多模式配置示例

1. 基础模式组合
java 复制代码
// 初始延迟3秒,之后每5秒执行
@Scheduled(initialDelay = 3000, fixedRate = 5000) 

// 每天8:30执行(使用属性配置)
@Scheduled(cron = "${app.schedule.daily-report}") 
2. 动态参数注入
properties 复制代码
application.properties
schedule.interval=5000
schedule.initial.delay=10000
schedule.cron.expression=0 0/15 * * * *
java 复制代码
@Scheduled(
    fixedRateString = "${schedule.interval}",
    initialDelayString = "${schedule.initial.delay}"
)
public void dynamicScheduleTask() {
    // 业务逻辑
}

@Scheduled(cron = "${schedule.cron.expression}")
public void cronTask() {
    // 定时任务
}

四、高级使用技巧

1. 多任务并行执行
java 复制代码
@Configuration
@EnableScheduling
public class ScheduleConfig implements SchedulingConfigurer {

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
        scheduler.setPoolSize(5);
        scheduler.setThreadNamePrefix("schedule-pool-");
        scheduler.initialize();
        taskRegistrar.setTaskScheduler(scheduler);
    }
}
2. 条件化调度
java 复制代码
@Profile("production")  // 仅生产环境生效
@ConditionalOnProperty(name = "scheduler.enabled", havingValue = "true")
@Scheduled(fixedRate = 10000)
public void conditionalTask() {
    // 生产环境专用任务
}
3. 分布式锁集成
java 复制代码
@Scheduled(cron = "0 0/30 * * * ?")
public void distributedTask() {
    String lockKey = "reportGenerationLock";
    try {
        if (redisLock.tryLock(lockKey, 300)) { // 获取30秒锁
            generateReport();
        }
    } finally {
        redisLock.release(lockKey);
    }
}

五、异常处理机制

1. 自定义异常处理器
java 复制代码
@ControllerAdvice
public class ScheduleExceptionHandler {

    @ExceptionHandler(ScheduleExecutionException.class)
    public void handleScheduleException(ScheduleExecutionException ex) {
        log.error("定时任务执行失败: {}", ex.getMessage());
        // 发送告警通知
        alertService.sendAlert("Schedule Failure", ex.getMessage());
    }
}
2. 重试机制
java 复制代码
@Retryable(
    value = {DataAccessException.class},
    maxAttempts = 3,
    backoff = @Backoff(delay = 1000, multiplier = 2)
)
@Scheduled(fixedDelay = 5000)
public void retryableTask() {
    // 可能失败的数据操作
    databaseService.batchUpdate();
}

六、常见问题解决方案

1. 任务不执行排查
markdown 复制代码
1. 检查主类是否添加`@EnableScheduling`
2. 确认任务方法为`public`修饰
3. 验证cron表达式有效性(可用在线验证工具)
4. 检查线程池是否被占满(默认单线程)
2. 多实例重复执行
java 复制代码
// 使用数据库锁方案示例
@Transactional
@Scheduled(cron = "0 0 3 * * ?")
public void exclusiveTask() {
    LocalDateTime now = LocalDateTime.now();
    ScheduleLock lock = lockRepo.findByTaskName("dailyCleanup");
    
    if (lock == null || lock.getLockUntil().isBefore(now)) {
        // 获取锁(设置30分钟有效期)
        lockRepo.save(new ScheduleLock("dailyCleanup", now.plusMinutes(30)));
        // 执行任务
        dataCleanupService.cleanup();
        // 释放锁
        lockRepo.deleteById("dailyCleanup");
    }
}

七、最佳实践建议

  1. 线程池配置原则

    properties 复制代码
    # 推荐配置公式
    IO密集型任务:线程数 = CPU核心数 * 2
    计算密集型任务:线程数 = CPU核心数 + 1
  2. 执行时间监控

    java 复制代码
    @Scheduled(fixedRate = 60000)
    public void monitoredTask() {
        StopWatch watch = new StopWatch();
        try {
            watch.start();
            // 业务逻辑
        } finally {
            watch.stop();
            if (watch.getTotalTimeMillis() > 5000) {
                log.warn("任务执行超时: {}ms", watch.getTotalTimeMillis());
            }
        }
    }
  3. 重要任务日志规范

    java 复制代码
    @Scheduled(cron = "0 0 2 * * ?")
    public void criticalTask() {
        log.info("==== 开始执行数据归档任务 ====");
        try {
            archiveService.archiveData();
            log.info("数据归档成功,归档数量: {}", count);
        } catch (Exception e) {
            log.error("数据归档失败", e);
            throw e;
        } finally {
            log.info("==== 结束数据归档任务 ====");
        }
    }

通过合理配置和遵循最佳实践,可以构建出高可靠、易维护的定时任务系统。特别注意在分布式环境下做好任务协调,避免重复执行导致的数据不一致问题。

相关推荐
曹牧5 分钟前
文档格式:OFD
java
豆角焖肉30 分钟前
Maven进阶与搭建私服
java·maven
大不点wow41 分钟前
Java序列化与反序列化:让对象走出JVM
java·开发语言·jvm
止语Lab1 小时前
好的 DX 不等于少写代码——三种语言的摩擦力设计课
后端
吃饱了得干活1 小时前
别再手动解析 LLM 输出了!LangChain 四种结构化输出方案对比
后端·python·langchain
噢,我明白了1 小时前
Java中日期和字符串的处理
java·开发语言·日期
程序员天天困1 小时前
Arthas trace 命令怎么用?一行定位最慢那行代码
jvm·后端
Huiturn1 小时前
GPT 5.6 连续编码 10 小时,纯 Python 啃下 Word 二进制格式——doc2docx 实现拆解
后端
dkbnull1 小时前
Spring Boot请求处理组件对比详解
java·spring boot
jun_bai1 小时前
Orthanc服务器使用java上传dicom影像文件
java·运维·服务器