定时任务

cron 表达式

cron表达式语法:秒 分 时 日 月 周 年(spring暂不支持年)

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 , - * /

特殊字符:

  • , 枚举

    (cron="7,9,23 * * * * ?") 表示任意时刻的 7、9、23秒启动这个任务。

  • - 范围

    (cron="7-20 * * * * ?") 表示7到20秒之间,每秒启动一次该任务。

  • / 步长

    (cron="7/5 * * * * ?") 表示第7秒启动,每5秒一次

    (cron="*/5 * * * * ?") 表示任意秒启动,每5秒一次

  • ? 出现在日和周的位置

    为了防止日和周冲突,在周和日上如果要写通配符使用 ?

    (cron="* * * 1 * ?") 每月的1号启动这个任务

  • L 出现在日和周的位置
    last:最后一个

    (cron="* * * ? * 3L") 每月的最后一个周二

  • W Work Day 工作日:

    (cron="* * * W * ?") 每个月的工作日触发

    (cron="* * * LW * ?") 每个月的最后一个工作日触发

  • # 第几个

    (cron="* * * ? * 5#2") 每个月的第二个周四

SpringBoot 整合定时任务

由于 SpringBoot 原生就支持定时任务,所以我们只需要使用 EnableScheduling 注解开启即可,当然还需要将他加入到容器中。

1
2
3
@Component
@EnableScheduling
public class HelloSchedule { ... }

使用 @Scheduled 注解创建一个定时任务,指定每分钟的第10秒启动,每5秒执行一次

1
2
3
4
5
6
7
8
9
10
11
@Slf4j
@Component
@EnableScheduling
public class HelloSchedule {

@Scheduled(cron = "10/5 * * * * ?")
private void hello() {
log.info("每分钟的第10秒启动,每5秒执行一次,当前是第 {} 秒", new Date().getSeconds());
}

}

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
每分钟的第10秒启动,每5秒执行一次,当前是第 10 秒
每分钟的第10秒启动,每5秒执行一次,当前是第 15 秒
每分钟的第10秒启动,每5秒执行一次,当前是第 20 秒
每分钟的第10秒启动,每5秒执行一次,当前是第 25 秒
每分钟的第10秒启动,每5秒执行一次,当前是第 30 秒
每分钟的第10秒启动,每5秒执行一次,当前是第 35 秒
每分钟的第10秒启动,每5秒执行一次,当前是第 40 秒
每分钟的第10秒启动,每5秒执行一次,当前是第 45 秒
每分钟的第10秒启动,每5秒执行一次,当前是第 50 秒
每分钟的第10秒启动,每5秒执行一次,当前是第 55 秒
每分钟的第10秒启动,每5秒执行一次,当前是第 10 秒
每分钟的第10秒启动,每5秒执行一次,当前是第 15 秒
每分钟的第10秒启动,每5秒执行一次,当前是第 20 秒
每分钟的第10秒启动,每5秒执行一次,当前是第 25 秒
每分钟的第10秒启动,每5秒执行一次,当前是第 30 秒
每分钟的第10秒启动,每5秒执行一次,当前是第 35 秒
每分钟的第10秒启动,每5秒执行一次,当前是第 40 秒
每分钟的第10秒启动,每5秒执行一次,当前是第 45 秒
每分钟的第10秒启动,每5秒执行一次,当前是第 50 秒
每分钟的第10秒启动,每5秒执行一次,当前是第 55 秒
每分钟的第10秒启动,每5秒执行一次,当前是第 10 秒
每分钟的第10秒启动,每5秒执行一次,当前是第 15 秒
每分钟的第10秒启动,每5秒执行一次,当前是第 20 秒

在 SpringBoot 中,周使用1-7分别表示周一到周日,个别定时任务每周的第一天是从周日开始。

阻塞

在 SpringBoot 中,定时任务是阻塞的,也就是需要等待上一次的定时任务完成后才能再继续执行定时任务。这是因为在 SpringBoot 中支持使用定时任务线程池,而这个线程池默认大小为 1 导致的任务阻塞,这一般在业务中是不被允许的。

org.springframework.boot.autoconfigure.task.TaskSchedulingProperties

TaskSchedulingProperties源码

在配置文件中修改线程池为合适的大小

1
spring.task.scheduling.pool.size=12

在某些版本可能不起作用…

异步任务

如果修改 scheduling 的线程池大小无效,那么还可以使用异步任务,这不仅仅可以用在定时任务上。

只需要使用 @EnableAsync 注解开启异步任务

1
2
3
4
5
@Slf4j
@Component
@EnableAsync
@EnableScheduling
public class HelloSchedule {}

并且在你需要异步执行的方法上再标注一个 @Async 注解即可

1
2
3
4
5
@Async
@Scheduled(cron = "10/5 * * * * ?")
public void hello() {
log.info("每分钟的第10秒启动,每5秒执行一次,当前是第 {} 秒", new Date().getSeconds());
}