IDE:IntelliJ IDEA 2022.2.3 x64
操作系统:win10 x64 位 家庭版
JDK: 1.8
提示:以下是本篇文章正文内容,下面案例可供参考
在我们日常开发中,开启并使用一个SpringBoot定时任务的大致步骤通常按如下所示
步骤
①在启动类上添加@EnableScheduling开启定时任务
示例代码如下
//开启定时任务 @EnableScheduling @SpringBootApplication public class MySpringBootApplication { public static void main(String[] args) { SpringApplication.run(MySpringBootApplication.class, args); } }
②编写定时任务,在其上添加@Scheduled设置任务执行时间
示例代码如下
//每3秒执行一次 @Scheduled(fixedRate = 3000) public void notifyA(){ String now = new SimpleDateFormat("yyy-MM-dd HH:mm:ss").format(new Date()); String name = Thread.currentThread().getName(); System.out.println(name+"通知已发送"+",发送时间为"+now); }
③将定时任务所在的类上添加@Component
示例代码如下
@Component public class springTimerTask { //每3秒执行一次 @Scheduled(fixedRate = 3000) public void notifyA(){ String now = new SimpleDateFormat("yyy-MM-dd HH:mm:ss").format(new Date()); String name = Thread.currentThread().getName(); System.out.println(name+"通知已发送"+",发送时间为"+now); } }
运行如下
在日常开发中,可以借助下面的网站快速生成cron表达式,提高开发效率
👉cron表达式在线生成器
形式 | * | * | * | * | * | * | * |
---|---|---|---|---|---|---|---|
字段 | Seconds | Minutes | Hours | DayofMonth | Month | DayofWeek | Year |
含义 | 秒 | 分 | 时 | 日 | 月 | 星期 | 年(可选) |
范围 | 0-59 | 0-59 | 0-23 | 1-31 | 1-12(JAN-DEC) | 1-7 (SUN-SAT) | |
符号 | , - * / | , - * / | , - * / | , - * / | , - * / ? L C # | , - * / | , - * / ? L C # |
备注
①DayofWeek字段:有效范围为1-7的整数或SUN-SAT两个范围。1表示星期天,2表示星期一,余下依次类推
②Year字段:有效范围为1970-2099年
举例说明
举例说明
注意
springBoot只支持cron表达式中的专有符号?,也仅仅支持6位的cron表达式,7位的不支持!!!
示例代码如下
//每2秒执行一次 @Scheduled(cron = "0/2 * * * * ?") public void notifyA(){ String now = new SimpleDateFormat("yyy-MM-dd HH:mm:ss").format(new Date()); String name = Thread.currentThread().getName(); System.out.println(name+"通知已发送"+",发送时间为"+now); }
运行如下
如下可知,@Schedule里还可以使用以下两种类型的表达式,分别是“fixedDelay = xxx” 与 “fixedRate = xxx”
fixedRate属性是@Schedule注解中的一个属性,它表示以固定的频率执行某个方法或任务。此属性用于指定任务执行的时间间隔,单位为毫秒。
例如,如果设置为2000(即2秒),则任务将每隔2秒执行一次。
示例代码如下
//每2秒执行一次 @Scheduled(fixedRate = 2000) public void notifyB(){ String now = new SimpleDateFormat("yyy-MM-dd HH:mm:ss").format(new Date()); String name = Thread.currentThread().getName(); System.out.println(name+"通知已发送"+",发送时间为"+now); }
运行如下
注意
fixedRate是从上一次方法执行开始的时间算起,如果上一次方法阻塞住了,下一次也是不会执行,但是在阻塞这段时间内累计应该执行的次数,当不再阻塞时,一下子把这些全部执行掉,而后再按照固定速率继续执行。
测试代码如下
//模拟任务阻塞4s,每2秒执行一次 @Scheduled(fixedRate = 2000) public void notifyB() throws InterruptedException { Thread.sleep(4000); String now = new SimpleDateFormat("yyy-MM-dd HH:mm:ss").format(new Date()); String name = Thread.currentThread().getName(); System.out.println(name+"通知已发送"+",发送时间为"+now); }
运行如下
fixedDelay属性是@Schedule注解中的一个属性,它表示以固定的时间间隔执行某个方法或任务,并在每次执行完成后等待指定的延迟时间再执行下一次。
fixedDelay属性用于指定任务执行之间的延迟时间,单位为毫秒。
例如,如果设置为2000(即2秒),则任务将每隔2秒执行一次,并且在每次执行完成后等待2秒再执行下一次。
示例代码如下
//每2秒执行一次,并延迟2s @Scheduled(fixedDelay = 2000) public void notifyB(){ String now = new SimpleDateFormat("yyy-MM-dd HH:mm:ss").format(new Date()); String name = Thread.currentThread().getName(); System.out.println(name+"通知已发送"+",发送时间为"+now); }
运行如下
注意
fixedDelay的执行时间是以上一次方法执行完开始算起,比如上一次方法执行阻塞住了,那么直到上一次执行完,并间隔给定的时间后,执行下一次
举个具体的例子,如果在一个方法上设置了fixedDelay=5*1000(即5秒),那么当该方法某一次执行结束后,开始计算时间,当时间达到5秒,就开始再次执行该方法。这意味着无论任务实际执行需要多长时间,每个任务之间的延迟时间始终保持不变。
测试代码如下
//模拟上次任务阻塞4s,然后间隔2秒执行下一次 @Scheduled(fixedDelay = 2000) public void notifyB() throws InterruptedException { Thread.sleep(4000); String now = new SimpleDateFormat("yyy-MM-dd HH:mm:ss").format(new Date()); String name = Thread.currentThread().getName(); System.out.println(name+"通知已发送"+",发送时间为"+now); }
运行如下
最简单的做法如下所示
步骤
①在启动类上添加注解@EnableAsync开启异步执行
示例代码如下
@EnableScheduling @SpringBootApplication @EnableAsync public class MySpringBootApplication { public static void main(String[] args) { SpringApplication.run(MySpringBootApplication.class, args); } }
②在定时任务上添加@Asynci设置异步执行
示例代码如下
@Component public class springTimerTask { //每2秒执行一次 @Async @Scheduled(cron = "0/2 * * * * ?") public void notifyA(){ String now = new SimpleDateFormat("yyy-MM-dd HH:mm:ss").format(new Date()); String name = Thread.currentThread().getName(); System.out.println(name+"通知已发送"+",发送时间为"+now); } //每3秒执行一次 @Async @Scheduled(cron = "0/3 * * * * ?") public void notifyB() throws InterruptedException { String now = new SimpleDateFormat("yyy-MM-dd HH:mm:ss").format(new Date()); String name = Thread.currentThread().getName(); System.out.println(name+"通知已发送"+",发送时间为"+now); } }
运行如下
why?
Spring默认单线程的定时任务,如果不开启异步,多个任务都是同一个线程在处理,如果这个线程内的任务比较耗时,会导致后续任务延期; 开启异步后,每个任务都会从线程池里分配一个线程去完成,避免耗时长的任务,导致其他任务延期,故而上述代码在中存在多个线程在执行同一个任务的情况;
不信?请看如下不开启异步,多个定时任务同时运行的情形
https://www.bilibili.com/video/BV1JR4y1N7Ni/?p=7&spm_id_from=333.1007.top_right_bar_window_history.content.click&vd_source=5a34715e416a427a73a3ca52397848b5
https://blog.csdn.net/u011066470/article/details/107529863?ops_request_misc=&request_id=&biz_id=102&utm_term=SpringBoot%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1:%20cron%E8%A1%A8%E8%BE%BE%E5%BC%8F&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-0-107529863.142v96pc_search_result_base9&spm=1018.2226.3001.4187