在程序开发的过程中,经常会使用到定时任务来实现一些功能,比如:
- 系统依赖于外部系统的非核心数据,可以定时同步(每天同步一次)
- 系统内部一些非核心数据的统计计算,可以定时计算(每天计算一次)
在Spring Boot中,我们可以使用@Scheduled注解来快速的实现定时任务。
@Scheduled注解主要支持以下3种方式:
- fixedRate 固定频率
- fixedDelay 固定延迟
- cron 自定义cron表达式
那么接下来,我们讲解下具体的实现方式。
1.fixedRate
首先,需要在启动类上添加@EnableScheduling注解:
package com.zwwhnly.springbootdemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling
public class SpringbootdemoApplication {
/*其他代码*/
public static void main(String[] args) {
SpringApplication.run(SpringbootdemoApplication.class, args);
}
}
然后,新建一个定时任务测试类TestSchedule,该类需要添加注解@Component,
最后,添加一个测试方法,该方法添加注解@Scheduled,为了能看到效果,我们每隔5秒输出下系统的当前时间,如下所示:
package com.zwwhnly.springbootdemo;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.util.Date;
@Component
public class TestSchedule {
private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// 每5秒执行一次
@Scheduled(fixedRate = 5000)
public void testFixedRate() {
System.out.println("当前时间:" + simpleDateFormat.format(new Date()));
}
}
启动项目,我们会看到运行结果如下:
从运行结果来看,确实是每隔5秒输出一次。
但是在实际项目中,不可能这么规律,比如方法的执行时间超过了5秒呢(这个应该很常见),那么彼时程序又是如何执行的呢?
我们先来修改下程序,让方法的执行时间超过5秒:
// 每5秒执行一次
@Scheduled(fixedRate = 5000)
public void testFixedRate() {
System.out.println("当前时间:" + simpleDateFormat.format(new Date()));
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
此时的运行结果为:
从运行结果,我们可以看出,现在是每6秒输出一次时间,由此我们可以得出结论:
如果方法的执行时间超过了定义的固定频率(比如5秒),那么上一次任务执行完成后,会立即执行下一次任务。
2.fixedDelay
添加一个新方法testFixedDelay,这里我们使用fixedDelay:
// 上次任务执行结束时间与下次任务执行开始的间隔时间为5s
@Scheduled(fixedDelay = 5000)
public void testFixedDelay()
{
System.out.println("当前时间:" + simpleDateFormat.format(new Date()));
}
启动项目,我们会看到运行结果如下:
当前时间:2019-04-09 10:28:56
当前时间:2019-04-09 10:29:01
当前时间:2019-04-09 10:29:06
当前时间:2019-04-09 10:29:11
当前时间:2019-04-09 10:29:16
当前时间:2019-04-09 10:29:21
当前时间:2019-04-09 10:29:26
当前时间:2019-04-09 10:29:31
当前时间:2019-04-09 10:29:36
当前时间:2019-04-09 10:29:41
当前时间:2019-04-09 10:29:46
当前时间:2019-04-09 10:29:51
当前时间:2019-04-09 10:29:56
当前时间:2019-04-09 10:30:01
看到运行结果,也许你会很疑惑,这不是和fixedRate的运行结果一样嘛,也是每隔5秒执行一次。
其实不然,否则Spring Boot怎么会同时支持fixedRate和fixedDelay呢,功能一样,还容易混淆。
3.fixedRate与fixedDelay的区别
为了让你更清晰的看到fixedRate与fixedDelay的区别,我们修改下fixedDelay方法,仍然是让它的执行时间超过5秒:
// 上次任务执行结束时间与下次任务执行开始的间隔时间为5s
@Scheduled(fixedDelay = 5000)
public void testFixedDelay() {
System.out.println("当前时间:" + simpleDateFormat.format(new Date()));
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
此时的运行结果为:
当前时间:2019-04-09 10:36:04
当前时间:2019-04-09 10:36:15
当前时间:2019-04-09 10:36:26
当前时间:2019-04-09 10:36:37
当前时间:2019-04-09 10:36:48
当前时间:2019-04-09 10:36:59
当前时间:2019-04-09 10:37:10
当前时间:2019-04-09 10:37:21
当前时间:2019-04-09 10:37:32
当前时间:2019-04-09 10:37:43
当前时间:2019-04-09 10:37:54
当前时间:2019-04-09 10:38:05
我们可以看出,现在两次输出时间的间隔为11秒,由此我们可以得出结论:
使用fixedDelay,上一次任务执行完成后,会延迟5秒,再执行下一次任务。
看到这里,是不是明白了fixedRate与fixedDelay的区别呢,通俗讲就是:
fixedRate是固定频率执行,fixedDelay是延迟固定时间执行。
4.cron
相比于上面讲的两种方式,cron表达式显得更加灵活,因为它基本满足各种场景的配置需求,比如固定频率执行,固定某个时间点执行等。
首先,我们使用cron表达式实现上述例子中的每隔5秒执行一次:
@Scheduled(cron = "0/5 * * * * ?")
public void testCron() {
System.out.println("当前时间:" + simpleDateFormat.format(new Date()));
}
运行结果为:
当前时间:2019-04-09 11:00:50
当前时间:2019-04-09 11:00:55
当前时间:2019-04-09 11:01:00
当前时间:2019-04-09 11:01:05
当前时间:2019-04-09 11:01:10
当前时间:2019-04-09 11:01:15
当前时间:2019-04-09 11:01:20
当前时间:2019-04-09 11:01:25
当前时间:2019-04-09 11:01:30
手动设置方法的执行时间超过5秒:
@Scheduled(cron = "0/5 * * * * ?")
public void testCron() {
System.out.println("当前时间:" + simpleDateFormat.format(new Date()));
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
此时的运行结果为:
当前时间:2019-04-09 11:16:15
当前时间:2019-04-09 11:16:25
当前时间:2019-04-09 11:16:35
当前时间:2019-04-09 11:16:45
当前时间:2019-04-09 11:16:55
当前时间:2019-04-09 11:17:05
当前时间:2019-04-09 11:17:15
当前时间:2019-04-09 11:17:25
当前时间:2019-04-09 11:17:35
如果想要设置成每天的某个时间点执行,比如我的个人博客http://www.zwwhnly.com/就是每晚20:00定时拉取GitHub代码并使用Jekyll编译到Nginx的目录下实现的自动发布。
实现这个配置的cron表达式为:0 0 20 * * ? 。
更多的cron表达式,可以到http://cron.qqe2.com/去自定义,勾选好会自动生成cron表达式,非常方便。
5.源码地址
https://github.com/zwwhnly/springbootdemo.git,欢迎大家下载,有问题可以多多交流。
6.参考链接
springboot使用@Scheduled做定时任务,以及连接池问题