经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Spring Boot » 查看文章
3分钟带你搞定Spring Boot中Schedule
来源:cnblogs  作者:潘志的研发笔记  时间:2024/7/19 10:37:48  对本文有异议

一、背景介绍

在实际的业务开发过程中,我们经常会需要定时任务来帮助我们完成一些工作,例如每天早上 6 点生成销售报表、每晚 23 点清理脏数据等等。

如果你当前使用的是 SpringBoot 来开发项目,那么完成这些任务会非常容易!

SpringBoot 默认已经帮我们完成了相关定时任务组件的配置,我们只需要添加相应的注解@Scheduled就可以实现任务调度!

二、方案实践

2.1、pom 包配置

pom包里面只需要引入Spring Boot Starter包即可!

  1. <dependencies>
  2. <!--spring boot核心-->
  3. <dependency>
  4. <groupId>org.springframework.boot</groupId>
  5. <artifactId>spring-boot-starter</artifactId>
  6. </dependency>
  7. <!--spring boot 测试-->
  8. <dependency>
  9. <groupId>org.springframework.boot</groupId>
  10. <artifactId>spring-boot-starter-test</artifactId>
  11. <scope>test</scope>
  12. </dependency>
  13. </dependencies>

2.2、启动类启用定时调度

在启动类上面加上@EnableScheduling即可开启定时

  1. @SpringBootApplication
  2. @EnableScheduling
  3. public class ScheduleApplication {
  4. public static void main(String[] args) {
  5. SpringApplication.run(ScheduleApplication.class, args);
  6. }
  7. }

2.3、创建定时任务

Spring Scheduler支持四种形式的任务调度!

  • fixedRate:固定速率执行,例如每5秒执行一次
  • fixedDelay:固定延迟执行,例如距离上一次调用成功后2秒执行
  • initialDelay:初始延迟任务,例如任务开启过5秒后再执行,之后以固定频率或者间隔执行
  • cron:使用 Cron 表达式执行定时任务
2.3.1、固定速率执行

你可以通过使用fixedRate参数以固定时间间隔来执行任务,示例如下:

  1. @Component
  2. public class SchedulerTask {
  3. private static final Logger log = LoggerFactory.getLogger(SchedulerTask.class);
  4. private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  5. /**
  6. * fixedRate:固定速率执行。每5秒执行一次。
  7. */
  8. @Scheduled(fixedRate = 5000)
  9. public void runWithFixedRate() {
  10. log.info("Fixed Rate Task,Current Thread : {},The time is now : {}", Thread.currentThread().getName(), dateFormat.format(new Date()));
  11. }
  12. }

运行ScheduleApplication主程序,即可看到控制台输出效果:

  1. Fixed Rate TaskCurrent Thread : scheduled-thread-1The time is now : 2020-12-15 11:46:00
  2. Fixed Rate TaskCurrent Thread : scheduled-thread-1The time is now : 2020-12-15 11:46:10
  3. ...
2.3.2、固定延迟执行

你可以通过使用fixedDelay参数来设置上一次任务调用完成与下一次任务调用开始之间的延迟时间,示例如下:

  1. @Component
  2. public class SchedulerTask {
  3. private static final Logger log = LoggerFactory.getLogger(SchedulerTask.class);
  4. private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  5. /**
  6. * fixedDelay:固定延迟执行。距离上一次调用成功后2秒后再执行。
  7. */
  8. @Scheduled(fixedDelay = 2000)
  9. public void runWithFixedDelay() {
  10. log.info("Fixed Delay Task,Current Thread : {},The time is now : {}", Thread.currentThread().getName(), dateFormat.format(new Date()));
  11. }
  12. }

控制台输出效果:

  1. Fixed Delay TaskCurrent Thread : scheduled-thread-1The time is now : 2020-12-15 11:46:00
  2. Fixed Delay TaskCurrent Thread : scheduled-thread-1The time is now : 2020-12-15 11:46:02
  3. ...
2.3.3、初始延迟任务

你可以通过使用initialDelay参数与fixedRate或者fixedDelay搭配使用来实现初始延迟任务调度。

  1. @Component
  2. public class SchedulerTask {
  3. private static final Logger log = LoggerFactory.getLogger(SchedulerTask.class);
  4. private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  5. /**
  6. * initialDelay:初始延迟。任务的第一次执行将延迟5秒,然后将以5秒的固定间隔执行。
  7. */
  8. @Scheduled(initialDelay = 5000, fixedRate = 5000)
  9. public void reportCurrentTimeWithInitialDelay() {
  10. log.info("Fixed Rate Task with Initial Delay,Current Thread : {},The time is now : {}", Thread.currentThread().getName(), dateFormat.format(new Date()));
  11. }
  12. }

控制台输出效果:

  1. Fixed Rate Task with Initial DelayCurrent Thread : scheduled-thread-1The time is now : 2020-12-15 11:46:05
  2. Fixed Rate Task with Initial DelayCurrent Thread : scheduled-thread-1The time is now : 2020-12-15 11:46:10
  3. ...
2.3.4、使用 Cron 表达式

Spring Scheduler同样支持Cron表达式,如果以上简单参数都不能满足现有的需求,可以使用 cron 表达式来定时执行任务。

关于cron表达式的具体用法,可以点击参考这里: https://cron.qqe2.com/

  1. @Component
  2. public class SchedulerTask {
  3. private static final Logger log = LoggerFactory.getLogger(SchedulerTask.class);
  4. private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  5. /**
  6. * cron:使用Cron表达式。每6秒中执行一次
  7. */
  8. @Scheduled(cron = "*/6 * * * * ?")
  9. public void reportCurrentTimeWithCronExpression() {
  10. log.info("Cron Expression,Current Thread : {},The time is now : {}", Thread.currentThread().getName(), dateFormat.format(new Date()));
  11. }
  12. }

控制台输出效果:

  1. Cron ExpressionCurrent Thread : scheduled-thread-1The time is now : 2020-12-15 11:46:06
  2. Cron ExpressionCurrent Thread : scheduled-thread-1The time is now : 2020-12-15 11:46:12
  3. ...

2.4、异步执行定时任务

在介绍异步执行定时任务之前,我们先看一个例子!

在下面的示例中,我们创建了一个每隔2秒执行一次的定时任务,在任务里面大概需要花费 3 秒钟,猜猜执行结果如何?

  1. @Component
  2. public class AsyncScheduledTask {
  3. private static final Logger log = LoggerFactory.getLogger(AsyncScheduledTask.class);
  4. private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  5. @Scheduled(fixedRate = 2000)
  6. public void runWithFixedDelay() {
  7. try {
  8. TimeUnit.SECONDS.sleep(3);
  9. log.info("Fixed Delay Task, Current Thread : {} : The time is now {}", Thread.currentThread().getName(), dateFormat.format(new Date()));
  10. } catch (InterruptedException e) {
  11. log.error("错误信息",e);
  12. }
  13. }
  14. }

控制台输入结果:

  1. Fixed Delay Task, Current Thread : scheduling-1 : The time is now 2020-12-15 17:55:26
  2. Fixed Delay Task, Current Thread : scheduling-1 : The time is now 2020-12-15 17:55:31
  3. Fixed Delay Task, Current Thread : scheduling-1 : The time is now 2020-12-15 17:55:36
  4. Fixed Delay Task, Current Thread : scheduling-1 : The time is now 2020-12-15 17:55:41
  5. ...

很清晰的看到,任务调度频率变成了每隔5秒调度一次!

这是为啥呢?

Current Thread : scheduling-1输出结果可以很看到,任务执行都是同一个线程!默认的情况下,@Scheduled任务都在 Spring 创建的大小为 1 的默认线程池中执行!

更直观的结果是,任务都是串行执行!

下面,我们将其改成异步线程来执行,看看效果如何?

  1. @Component
  2. @EnableAsync
  3. public class AsyncScheduledTask {
  4. private static final Logger log = LoggerFactory.getLogger(AsyncScheduledTask.class);
  5. private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  6. @Async
  7. @Scheduled(fixedDelay = 2000)
  8. public void runWithFixedDelay() {
  9. try {
  10. TimeUnit.SECONDS.sleep(3);
  11. log.info("Fixed Delay Task, Current Thread : {} : The time is now {}", Thread.currentThread().getName(), dateFormat.format(new Date()));
  12. } catch (InterruptedException e) {
  13. log.error("错误信息",e);
  14. }
  15. }
  16. }

控制台输出结果:

  1. Fixed Delay Task, Current Thread : SimpleAsyncTaskExecutor-1 : The time is now 2020-12-15 18:55:26
  2. Fixed Delay Task, Current Thread : SimpleAsyncTaskExecutor-2 : The time is now 2020-12-15 18:55:28
  3. Fixed Delay Task, Current Thread : SimpleAsyncTaskExecutor-3 : The time is now 2020-12-15 18:55:30
  4. ...

任务的执行频率不受方法内的时间影响,以并行方式执行!

2.5、自定义任务线程池

虽然默认的情况下,@Scheduled任务都在 Spring 创建的大小为 1 的默认线程池中执行,但是我们也可以自定义线程池,只需要实现SchedulingConfigurer类即可!

自定义线程池示例如下:

  1. @Configuration
  2. public class SchedulerConfig implements SchedulingConfigurer {
  3. @Override
  4. public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
  5. ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
  6. //线程池大小为10
  7. threadPoolTaskScheduler.setPoolSize(10);
  8. //设置线程名称前缀
  9. threadPoolTaskScheduler.setThreadNamePrefix("scheduled-thread-");
  10. //设置线程池关闭的时候等待所有任务都完成再继续销毁其他的Bean
  11. threadPoolTaskScheduler.setWaitForTasksToCompleteOnShutdown(true);
  12. //设置线程池中任务的等待时间,如果超过这个时候还没有销毁就强制销毁,以确保应用最后能够被关闭,而不是阻塞住
  13. threadPoolTaskScheduler.setAwaitTerminationSeconds(60);
  14. //这里采用了CallerRunsPolicy策略,当线程池没有处理能力的时候,该策略会直接在 execute 方法的调用线程中运行被拒绝的任务;如果执行程序已关闭,则会丢弃该任务
  15. threadPoolTaskScheduler.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
  16. threadPoolTaskScheduler.initialize();
  17. scheduledTaskRegistrar.setTaskScheduler(threadPoolTaskScheduler);
  18. }
  19. }

我们启动服务,看看cron任务示例调度效果:

  1. Cron ExpressionCurrent Thread : scheduled-thread-1The time is now : 2020-12-15 20:46:00
  2. Cron ExpressionCurrent Thread : scheduled-thread-2The time is now : 2020-12-15 20:46:06
  3. Cron ExpressionCurrent Thread : scheduled-thread-3The time is now : 2020-12-15 20:46:12
  4. Cron ExpressionCurrent Thread : scheduled-thread-4The time is now : 2020-12-15 20:46:18
  5. ....

当前线程名称已经被改成自定义scheduled-thread的前缀!

三、小结

本文主要围绕Spring scheduled应用实践进行分享,如果是单体应用,使用SpringBoot内置的@scheduled注解可以解决大部分业务需求,上手非常容易!

项目源代码地址:spring-boot-example-scheduled

四、参考

1、https://springboot.io/t/topic/2758

原文链接:https://www.cnblogs.com/dxflqm/p/18310855

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号