SpringBoot集成了@Scheduled的相关依赖(org.springframework.scheduling.annotation.Scheduled);我们只需要直接使用即可。
@Scheduled注解的使用步骤:
第一步:在启动类上面启用定时任务
第二步:在要定时执行的方法上面,加上@Scheduled注解,并指定执行间隔
第三步:把@Scheduled所在的类注入到容器中
第四步:启动启动类(注:启动启动类之后,定时任务就开始了)
注意:Spring的定时任务默认是单线程的。如果有多个定时任务,那么执行起来时间会有问题;这时就需要开启多线程了(先把基本知识介绍完,本文末尾再介绍线程问题)。
注:如果只有一个定时任务,那么我们可以将这个定时任务单独拿出来作为一个微服务;这样一来就不需要开启多线程了。
@Scheduled的定时策略:
cronExpression定义时间规则,cron表达式由6或7个空格分隔的时间字段组成: 秒 分 时 日 月 星期 年(可选);
注:如果Spring中使用的是Quartz等的话,那么cron表达式是支持七位的;如果Spring中使用的是Spring Task等的话,
那么cron表达式只支持六位。
字段
允许值
允许的特殊字符
秒
0-59
, - * /
分
0-59
, - * /
时
0-23
, - * /
日
1-31
, - * ? / L W C
月
1-12 或 JAN-DEC
, - * /
星期
1-7 或 SUN-SAT
注:1是星期天,7是星期六
, - * ? / L C #
年
1970-2099
, - * /
特殊字符:
/ : 指定每隔多久执行一次 如: 0/5 * * * * ? : 每5秒执行一次
注:此时/的“分子”位置的值为:定义此次计划执行时间允许值的最小值,如:
34/10 * * * * ? : 表示此次计划只在34,44,54秒时执行,在4,14,24秒时不会执行。
注: */10 等价于 0/10
* : 通配,每个单位时间都要执行 如:"*"在分钟的字段域里表示“每分钟”
? : 只在日期域或者星期域中使用 使用场景示例:月份中的日期和星期中的日期这两个元素时互斥的时候应该通过设置一个问号来表明不想设置那个字段。
- : 用来指定一个范围,在这个时间范围内,就要执行。
如:10-12 在小时域意味着 10点、11点、12点,那么在这个时间域内要执行Scheduled计划
, : 用来指定多个时间值(一般用于不相邻的时间)
如:0,13,45 * * * * ?表示 在第0秒,13秒,45秒时要执行Scheduled计划。
# : 只允许在星期域中出现。
如:6#3表示本月第三周的星期五(6表示星期五,3表示第三周)。
L : last的省略写法
如果出现在日域,那么表示一个月的最后一天
如: 0 * * L 3 ? 表示 三月的最后一天,每小时每分钟都要执行一次
如果出现在星期域,那么表示一个月的最后一个星期几
如: 0 * * * 3 7L 表示 三月的最后一个星期六(注:7表示星期六),每小时每分钟都要执行一次
W : 指向该日,如果该日不是工作日,那么指向本月里离得最近的一个工作日。
0 * * 15W * ? 表示每个月的15号为工作日(注:如果15号为节假日的话,那么指向离它最近的一个本月内的工作日)。
C : 允许在日期域和星期域出现。
示例:
"0 0 10,14,16 * * ?"
每天上午10点,下午2点,4点
"0 0/30 9-17 * * ?"
朝九晚五工作时间内每半小时
"0 0 12 ? * WED"
表示每个星期三中午12点
"0 0 12 * * ?"
每天中午12点触发
"0 15 10 ? * *"
每天上午10:15触发
"0 15 10 * * ?"
每天上午10:15触发
"0 15 10 * * ? *"
每天上午10:15触发
"0 15 10 * * ? 2005"
2005年的每天上午10:15触发
"0 * 14 * * ?"
在每天下午2点到下午2:59期间的每1分钟触发
"0 0/5 14 * * ?"
在每天下午2点到下午2:55期间的每5分钟触发
"0 0/5 14,18 * * ?"
在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
"0 0-5 14 * * ?"
在每天下午2点到下午2:05期间的每1分钟触发
"0 10,44 14 ? 3 WED"
每年三月的星期三的下午2:10和2:44触发
"0 15 10 ? * MON-FRI"
周一至周五的上午10:15触发
"0 15 10 15 * ?"
每月15日上午10:15触发
"0 15 10 L * ?"
每月最后一日的上午10:15触发
"0 15 10 ? * 6L"
每月的最后一个星期五上午10:15触发
"0 15 10 ? * 6L 2002-2005"
2002年至2005年的每月的最后一个星期五上午10:15触发
"0 15 10 ? * 6#3"
每月的第三个星期五上午10:15触发
线程问题:
在同一个线程中,如果执行某一个定时任务所需时间较长,那么就可能影响其他所需时间较短的定时任务。
即:当所需时间较长的方法抢占到了线程;在其还没有运行完时,其他的定时任务错过了“定时”。
如:
控制台打印出的结果为:
使用多线程解决此问题:
配置线程池(SpringBoot配置线程池无需引入其他依赖,直接使用注解即可):
给出文字版:
//线程池配置
@Configuration
@EnableAsync
public class ThreadPoolConfig {
@Bean
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 配置核心线程数
executor.setCorePoolSize(5);
// 配置最大线程数
executor.setMaxPoolSize(5);
// 配置队列大小
executor.setQueueCapacity(99999);
// 配置线程池中的线程的名称前缀
executor.setThreadNamePrefix("async-executor-");
// rejection-policy:当pool已经达到max size的时候,如何处理新任务
// CALLER_RUNS:不在新线程中执行任务,而是由调用者所在的线程来执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 执行初始化
executor.initialize();
return executor;
}
}
在有@Scheduled定时计划的方法上,使用线程池:
此时,控制台打印出的信息为:
如上图所示:方法Two每5秒执行一次;方法One每10秒执行一次(截图我只截了一点,下面的也遵循这个规律)
由此可见:开启多线程使用线程池后,解决了之前的问题。
相关文章
- 01-05Spring Boot 与 R2DBC 整合
- 01-05spring boot 定时器
- 01-05spring boot 整合 RabbitMQ 错误
- 01-05Spring Boot整合Mybatis及PageHelper实现分页查询
- 01-05【Spring Boot学习之十二】整合mybatis3
- 01-05spring boot-整合CAS Client实现单点登陆验证
- 01-05Spring Boot整合@Scheduled定时计划
- 01-05spring注解 @Scheduled(cron = "0 0 1 * * *")实现定时的执行任务
- 01-05Spring整合Quartz定时任务 在集群、分布式系统中的应用
- 01-05kafka 整合 spring boot 与 avro