SpringBoot定时任务@Scheduled注解详解
项目开发中,经常会遇到定时任务的场景,Spring提供了@Scheduled注解,方便进行定时任务的开发。要使用@Scheduled注解,首先需要在启动类添加@EnableScheduling,启用Spring的计划任务执行功能,这样可以在容器中的任何Spring管理的bean上检测@Scheduled注解,执行计划任务。
注解定义
@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 ""; }
参数说明
代码示例:
@Slf4j @Component public class RunIntervalTestScheduler { @Scheduled(cron = "0/1 * * * * ?") public void singleThreadTest1() { log.info("singleThreadTest1"); LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1)); } @Scheduled(cron = "0/1 * * * * ?") public void singleThreadTest2() { log.info("singleThreadTest2"); LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1)); } @Scheduled(cron = "0/1 * * * * ?") public void singleThreadTest3() { log.info("singleThreadTest3"); LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1)); } }
执行结果:
可以看到,默认情况下,三个任务串行执行,都使用pool-1-thread-1同一个线程池,并且线程只有一个。可以通过实现SchedulingConfigurer接口,手动创建线程池,配置期望的线程数量。
示例代码:
@Configuration public class ScheduledConfig implements SchedulingConfigurer { /** * 任务执行线程池大小 */ private static final int TASK_POOL_SIZE = 50; /** * 线程名 */ private static final String TASK_THREAD_PREFIX = "test-task-"; @Override public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) { ThreadPoolTaskScheduler taskPool = new ThreadPoolTaskScheduler(); taskPool.setPoolSize(TASK_POOL_SIZE); taskPool.setThreadNamePrefix(TASK_THREAD_PREFIX); taskPool.initialize(); scheduledTaskRegistrar.setTaskScheduler(taskPool); } }
任务执行结果:
此时任务的执行已经异步化,从自定义线程池中分配线程执行任务,在实际应用中需要考虑实际任务数量,创建相应大小的线程池
fixedRate/fixedDelay区别
- fixedRate是配置上一次任务执行开始到下一次执行开始的间隔时间,不会等待上一次任务执行完成就会调度下一次任务,将其放入等待队列中
代码示例:
@Slf4j @Component public class RunIntervalTestScheduler { @Scheduled(initialDelay = 1000, fixedRate = 1000) public void fixedRate() throws Exception { log.info("fixedRate run"); TimeUnit.SECONDS.sleep(3); } }
执行结果:
任务配置的fixedRate为1s,执行日志打印的时间间隔都是3s左右,也就是上一次执行完成后,紧接着就执行下一次任务。fixedDelay是配置的上一次任务执行结束到下一次执行开始的间隔时间,也就是说会等待上一次任务执行结束后,延迟间隔时间,再执行下一次任务
代码示例:
@Slf4j @Component public class RunIntervalTestScheduler { @Scheduled(initialDelay = 1000, fixedDelay = 1000) public void fixedDelay() throws Exception { log.info("fixedDelay run"); TimeUnit.SECONDS.sleep(3); } }
执行结果:
任务配置的fixedDelay为1s,执行日志打印的时间间隔都是4s左右,也就是上一次执行完成后,延迟1s后执行下一次任务
- cron表达式如果配置为类似每秒执行、每分钟执行(例:0/1 * * * * ?, 每秒执行),调度跟fixedDelay是一致的,也是在上一次任务执行结束后,等待间隔时间
代码示例:
@Slf4j @Component public class RunIntervalTestScheduler { @Scheduled(cron = "0/1 * * * * ?") public void cronRun() throws Exception{ log.info("cron run"); TimeUnit.SECONDS.sleep(3); } }
执行结果:
执行日志打印的时间间隔都是4s左右,也就是上一次执行完成后,延迟1s后执行下一次任务
- cron表达式如果配置为固定时间执行(例:1 * * * * ?, 秒数为1时执行),若上一次任务没有执行完,则不会调度本次任务,跳过本次执行,等待下一次执行周期
代码示例:
@Slf4j @Component public class RunIntervalTestScheduler { @Scheduled(cron = "1 * * * * ?") public void cronRun() throws Exception{ log.info("cron run"); TimeUnit.SECONDS.sleep(70); } }
执行结果:
上一次任务未执行完毕,则跳过了本次执行。参考文档:https://blog.csdn.net/hu_dongyang/article/details/114270232
郭慕荣博客园