Spring Boot 定时任务与 Cron:表达式、时区和执行边界
整理 Cron 字段与 Spring 定时任务配置,区分不同调度器的表达式规则,并补充时区、执行耗时和多实例运行的检查方法。
不能把 Linux crontab、Spring 和 Quartz 的表达式直接互换。确认字段数量、时区、任务耗时和实例数量。一次定时触发不等于业务只执行一次,重复执行的业务影响需要单独处理。
一. 基本介绍
1. 描述
官网地址,cron表达式是由6个或7个字段组成的字符串,这些字段由空格分隔。字段可以包含任何允许的值,以及该字段允许的特殊字符的各种组合,使用方式为
* * * * ? *(代表启动时每一秒都执行一次),其中每个字段含义如下:
| Field Name(字段名) | Mandatory(是否必传) | Allowed Values(允许值) | Allowed Special Characters(特殊字符) |
|---|---|---|---|
| Seconds | YES | 0-59 | , - * / |
| Minutes | YES | 0-59 | , - * / |
| Hours | YES | 0-23 | , - * / |
| Day of month | YES | 1-31 | , - * ? / L W |
| Month | YES | 1-12 or JAN-DEC | , - * / |
| Day of week | YES | 1-7 or SUN-SAT | , - * ? / L # |
| Year | NO | empty, 1970-2099 | , - * / |
2. 特殊字符含义
| 特殊字符 | 含义 | 示例 |
|---|---|---|
* |
所有可能的值。 | 在月域中,*表示每个月;在星期域中,*表示星期的每一天。 |
, |
列出枚举值。 | 在分钟域中,5,20表示分别在5分钟和20分钟触发一次。 |
- |
范围。 | 在分钟域中,5-20表示从5分钟到20分钟之间每隔一分钟触发一次。 |
/ |
指定数值的增量。 | 在分钟域中,0/15表示从第0分钟开始,每15分钟。在分钟域中3/20表示从第3分钟开始,每20分钟。 |
? |
不指定值,仅日期和星期域支持该字符。 | 当日期或星期域其中之一被指定了值以后,为了避免冲突,需要将另一个域的值设为?。 |
L |
单词Last的首字母,表示最后一天,仅日期和星期域支持该字符。说明 指定L字符时,避免指定列表或者范围,否则,会导致逻辑问题。 |
在日期域中,L表示某个月的最后一天。在星期域中,L表示一个星期的最后一天,也就是星期日(SUN)。如果在L前有具体的内容,例如,在星期域中的6L表示这个月的最后一个星期六。 |
W |
除周末以外的有效工作日,在离指定日期的最近的有效工作日触发事件。W字符寻找最近有效工作日时不会跨过当前月份,连用字符LW时表示为指定月份的最后一个工作日。 |
在日期域中5W,如果5日是星期六,则将在最近的工作日星期五,即4日触发。如果5日是星期天,则将在最近的工作日星期一,即6日触发;如果5日在星期一到星期五中的一天,则就在5日触发。 |
# |
确定每个月第几个星期几,仅星期域支持该字符。 | 在星期域中,4#2表示某月的第二个星期四。 |
3. 取值示例
| 示例 | 说明 |
|---|---|
0 15 10 ? * * |
每天上午10:15执行任务 |
0 15 10 * * ? |
每天上午10:15执行任务 |
0 0 12 * * ? |
每天中午12:00执行任务 |
0 0 10,14,16 * * ? |
每天上午10:00点、下午14:00以及下午16:00执行任务 |
0 0/30 9-17 * * ? |
每天上午09:00到下午17:00时间段内每隔半小时执行任务 |
0 * 14 * * ? |
每天下午14:00到下午14:59时间段内每隔1分钟执行任务 |
0 0-5 14 * * ? |
每天下午14:00到下午14:05时间段内每隔1分钟执行任务 |
0 0/5 14 * * ? |
每天下午14:00到下午14:55时间段内每隔5分钟执行任务 |
0 0/5 14,18 * * ? |
每天下午14:00到下午14:55、下午18:00到下午18:55时间段内每隔5分钟执行任务 |
0 0 12 ? * WED |
每个星期三中午12:00执行任务 |
0 15 10 15 * ? |
每月15日上午10:15执行任务 |
0 15 10 L * ? |
每月最后一日上午10:15执行任务 |
0 15 10 ? * 6L |
每月最后一个星期六上午10:15执行任务 |
0 15 10 ? * 6#3 |
每月第三个星期六上午10:15执行任务 |
0 10,44 14 ? 3 WED |
每年3月的每个星期三下午14:10和14:44执行任务 |
0 15 10 ? * * 2022 |
2022年每天上午10:15执行任务 |
0 15 10 ? * * * |
每年每天上午10:15执行任务 |
0 0/5 14,18 * * ? 2022 |
2022年每天下午14:00到下午14:55、下午18:00到下午18:55时间段内每隔5分钟执行任务 |
0 15 10 ? * 6#3 2022,2023 |
2022年至2023年每月第三个星期六上午10:15执行任务 |
0 0/30 9-17 * * ? 2022-2025 |
2022年至2025年每天上午09:00到下午17:30时间段内每隔半小时执行任务 |
0 10,44 14 ? 3 WED 2022/2 |
从2022年开始,每隔两年3月的每个星期三下午14:10和14:44执行任务 |
二. springboot使用
1. 基本使用
如果不会写复杂的表达式,可以到cron在线网站根据自己的需求生成(百度CRON在线生成即可)
/**
* @EnableScheduling 开启定时任务功能
*/
@Component
@EnableScheduling
public class ScheduledTask {
//启动时,每秒都执行一次
@Scheduled(cron = "* * * * * *")
public void testScheduledTask(){
System.out.println("定时任务");
}
}
2.注意事项
- spring只支持cron的6位参数,设置7为参数启动会报错
Encountered invalid @Scheduled method 'testScheduledTask': Cron expression must consist of 6 fields (found 7 in "* * * * * * *")
-
spring中对第六位参数的使用,1-7为周一到周日,和官方文档有些许差别
-
任务的实际触发间隔取决于 cron、fixedDelay、fixedRate、调度器配置及执行耗时,不能一概推导成固定的 4 秒。
@Component @EnableScheduling @Slf4j public class ScheduledTask { @Scheduled(cron = "* * * * * *") public void testScheduledTask() throws InterruptedException { log.info("定时任务"); TimeUnit.SECONDS.sleep(3); } }解决方案:
-
将业务代码采用异步执行的方式。
-
使用Scheduled任务的线程池(有些版本会失效),TaskSchedulingAutoConfiguration
spring.task.scheduling.pool.size=10 #配置定时任务的线程池 -
开启异步任务
@Component @EnableAsync//开启异步任务功能 @EnableScheduling @Slf4j public class ScheduledTask { @Async//该方法异步执行 @Scheduled(cron = "* * * * * *") public void testScheduledTask() throws InterruptedException { log.info("定时任务"); TimeUnit.SECONDS.sleep(3); } }
调度方式需要分开验证
Spring 的 cron 表达式与 Linux crontab 不同。fixedDelay 关注前次完成到下次开始的间隔,fixedRate 关注周期调度;任务超时或线程资源不足时仍需观察真实执行行为。增加线程数或加 @Async 之前,应先明确任务是否允许重叠以及业务是否幂等。
- 本文标签: Java
- 本文链接: https://www.tianyajuanke.top/article/19
- 版权声明: 本文由吴沛芙原创发布,转载请遵循《署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)》许可协议授权