在项目开发过程中,经常需要定时任务来帮助我们来做一些内容,springboot 默认已经帮我们实行了,开发者只需要添加相应的注解就可以实现。
一、Spring Task 定时任务
1.1 静态定时任务(基于 @Scheduled 注解)
1.1.1 pom 配置
pom 包里面只需要引入springboot starter
包即可:
1 | <dependency> |
1.1.2 启动类启用定时
在启动类上面加上@EnableScheduling
即可开启定时:
1 |
|
注意:这里的@EnableScheduling
注解,它的作用是发现注解@Scheduled
的任务并由后台执行。没有它的话将无法执行定时任务。
引用官方文档原文:
@EnableScheduling
ensures that a background task executor is created. Without it, nothing gets scheduled.官方文档地址:
http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#scheduling-enable-annotation-support
1.1.3 创建定时任务实现类
定时任务1:每过 6 秒执行
1 |
|
定时任务2:每过 6 秒执行
注意:上一次执行完毕时间点之后 6 秒再执行,不会等待上一个定时任务执行完毕再启动下一个定时任务,不论上次定时任务执行时间是多少。
1 |
|
定时任务3:每过上一个定时任务执行完毕之后的 6 秒执行
注意:上一次执行完毕时间点之后 6 秒再执行,等待上一次定时任务执行完毕之后再间隔 6 秒执行下一个定时任务。
1 |
|
1.1.4 参数说明
@Scheduled
参数可以接受两种定时的设置每隔 6 秒执行定时任务:
一种是我们常用的cron="*/6 * * * * ?"
,另一种是 fixedRate = 6000
fixedRate 说明
fixedRate
:上一次开始执行时间点之后再执行,参数类型为 long,单位 ms;fixedRateString
:与fixedRate
的含义一样,只是将参数类型变为 String;fixedDelay
:上一次执行完毕时间点之后再执行,参数类型为 long,单位 ms;fixedDelayString
:与fixedDelay
含义一样,只是参数类型变为 String;initialDelay
:表示延迟多久再第一次执行任务,参数类型为 long,单位 ms;initialDelayString
:与initialDelay
的含义一样,只是将参数类型变为 String;zone
:时区,默认为当前时区,一般没有用到。
1.1.5 Cron 表达式
Cron 表达式有专门的语法,而且感觉有点绕人,不过简单来说,大家记住一些常用的用法即可,特殊的语法可以单独去查。
Cron 表达式是由一串字符串表示,使用数字+空格+特殊字符
的形式组合成完整表达式,Cron 表达式由空格将其划分为 6 或 7 个域,每一个域代表一个含义解释:
1.1.5.1 域解释
1 | * 第一位,表示秒,取值:0-59 |
注意:第六位的取值:1-7 表示的是星期一至星期日
1.1.5.2 特殊符号解释
(*)星号
可以理解为每的意思,每秒,每分,每天,每月,每年;
(?)问号
只能用在每月第几天和星期两个域。表示不指定值,当 2 个子表达式其中之一被指定了值以后,为了避免冲突,需要将另一个子表达式的值设为?
;
(-)减号
表达一个范围,如在小时字段中使用 “10-12”,则表示从10到12点,即 10, 11, 12;
(,)逗号
表达一个列表值,如在星期字段中使用 “1, 2, 4”,则表示星期一,星期二,星期四,也可以使用单词缩写来指定,例如:”MON,WED,FRI” 在星期域里表示 “星期一、星期三、星期五;
(/)斜杠
表示起始时间开始触发,然后每隔固定时间触发一次,例如在分域使用 5/20 ,则意味着5分,25分,45分,分别触发一次,另外:*/y
,等同于0/y
;
(L)字符
表示最后,只能出现在星期和每月第几天域,如果在星期域使用 1L,意味着在最后的一个星期日触发;
(W)字符
表示有效工作日(周一到周五),只能出现在每月第几日域,系统将在离指定日期的最近的有效工作日触发事件。注意一点,W 的最近寻找不会跨过月份; LW : 这两个字符可以连用,表示在某个月最后一个工作日,即最后一个星期五。
(#)字符
用于确定每个月第几个星期几,只能出现在每月第几天域,例如在1#3,表示某月的第三个星期日。
官方例子:
1 | "0 0 * * * *" 表示每小时0分0秒执行一次 |
1.2 动态定时任务(基于 SchedulingConfigurer 接口)
为了演示效果,这里选用 Mysql 数据库保存 Cron 表达式,使用 Mybatis 框架来查询和调整定时任务的执行周期,然后观察定时任务的执行情况。当然,可以使用静态配置文件的形式配置。
1.2.1 pom 配置
1 | <dependencies> |
1.2.2 添加数据库记录
在 mysql 数据库中创建socks
数据库,并创建cron
数据库表:
1 | DROP DATABASE IF EXISTS `socks`; |
在项目中的application.yml
添加数据源:
1 | spring: |
1.2.3 创建定时器
数据库准备好数据之后,编写要定时执行的任务类,实现SchedulingConfigurer
接口,重写configureTasks
方法:
1 | import java.text.SimpleDateFormat; |
三、Timer 定时任务
这个API
目前在项目中很少用,直接给出示例代码。具体的介绍可以查看API
。Timer
的内部只有一个线程,如果多个任务的话就会顺序执行,这样任务的延迟时间和循环时间就会出现问题。
1 | import java.util.Timer; |
观察测试结果,能够发现TimerTask
配置的任务,每隔10s
被执行了一次,执行线程默认都是Timer-0
这个线程,并且启动的时机是 new 出 Timer 并执行schedule()
方法的时候。
1 | 17:42:48.576 [Timer-0] INFO org.woodwhale.king.service.TimerService - Schedule timerTask 1 times |
四、ScheduledExecutorService 定时任务
ScheduledExecutorService
是 延时执行 的线程池,对于 多线程 环境下的 定时任务,推荐用 ScheduledExecutorService
代替 Timer
定时器。
4.1 等待定时任务执行完毕再进行下一次定时任务
创建一个线程数量为4
的任务线程池,同一时刻并向它提交4
个定时任务,用于测试延时任务的并发处理。执行ScheduledExecutorService
的scheduleWithFixedDelay()
方法,设置任务线程池的初始任务延迟时间为2
秒,并在上一次执行完毕时间点之后2
秒再执行下一次任务。
1 | public void scheduleWithFixedDelay() { |
测试结果如下,我们可以发现每隔2
秒的时间间隔,就会有4
个定时任务同时执行。因为在任务线程池初始化时,我们同时向线程池提交了4
个任务,这四个任务会完全利用线程池中的4
个线程进行任务执行。
4.2 固定时间进行定时任务
创建一个线程数量为4
的任务线程池,同一时刻并向它提交4
个定时任务,用于测试延时任务的并发处理。每个任务分别执行ScheduledExecutorService
的scheduleAtFixedRate()
方法,设置任务线程池的初始任务延迟时间为2
秒,并在上一次开始执行时间点之后2
秒再执行下一次任务。
1 | public void scheduleAtFixedRate() { |
测试结果如下,我们可以发现每隔 2
秒的时间间隔,就会有 4
个定时任务同时执行,因为在任务线程池初始化时,我们同时向线程池提交了 4
个任务,这 四个任务会完全利用线程池中的 4
个线程进行任务执行。
五、配置任务线程池(实现多线程并发处理)
上述配置都是基于单线程的任务调度,如何引入多线程提高延时任务的并发处理能力?
Spring Boot
提供了一个SchedulingConfigurer
配置接口。我们通过 ScheduleConfig
配置文件实现ScheduleConfiguration
接口,并重写 configureTasks()
方法,向ScheduledTaskRegistrar
注册一个ThreadPoolTaskScheduler
任务线程对象即可。
1 | import org.slf4j.Logger; |
参考博文:
SpringBoot 创建定时任务(配合数据库动态执行)https://www.jianshu.com/p/d160f2536de7
springboot(九):定时任务https://www.cnblogs.com/ityouknow/p/6132645.html
spring boot项目中处理Schedule定时任务https://www.rjkf.cn/springboot-schedule-cron/
SpringBoot定时任务及Cron表达式详解https://my.oschina.net/jack90john/blog/1506474
实战Spring Boot 2.0系列(六) - 单机定时任务的几种实现https://juejin.im/post/5b31b9eff265da598826c200