Spring注解配置定时任务,运行时间超出间隔时间

467 阅读5分钟

结论

@EnableScheduling
@Scheduled有三种配置方式:

  • cron
  • fixedRate
  • fixedDelay

cron: 是固定时间进行处理,如果执行时长超过间隔,则直接跳过这次任务
fixedDelay: 是基于上一次任务结束的时间来计算下一次任务的开始时间
fixedRate: 则是基于上一次任务开始的时间来计算下一次任务的开始时间,如果执行时长超过间隔,那么会在任务执行结束后立刻执行下一次,除非用@Async注解

这篇文章主要探讨的是如果任务执行时间超过了定时任务间隔时间,定时任务会如何执行。

cron

 @EnableScheduling
 @SpringBootApplication
 public class SpringbootApplication {
	 public static void main(String[] args) {
	 	SpringApplication.run(SpringbootApplication.class, args);
	 }
         
	 @Scheduled(cron = "*/4 * * * * *")
	 public String ssss(){
		 String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
           System.out.println("task3 " + date);
		 try {
		 	Thread.sleep(6000);
		 }catch (Exception e){
		 	e.printStackTrace();
		 }
		 return null;
	 }
 }

执行结果:

[scheduling-5] task3 2024-05-11 16:54:04
[scheduling-2] task3 2024-05-11 16:54:12
[scheduling-4] task3 2024-05-11 16:54:20
[scheduling-4] task3 2024-05-11 16:54:28
[scheduling-4] task3 2024-05-11 16:54:36
[scheduling-2] task3 2024-05-11 16:54:44
[scheduling-4] task3 2024-05-11 16:54:52
[scheduling-4] task3 2024-05-11 16:55:00

结论
cron都是按照整点来运行的,比如4秒一次,他会在04/08/12/16/20/24/28/32/36/40/44/48/52/56/00…秒运行,如果那个时间点还没运行结束,那么就会跳过这次任务。

fixedRate

 //省略部分代码
 @Scheduled(fixedRate = 4000)
 public String ssss(){
	  String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
           System.out.println("task4 " + date);
	 try {
	 	Thread.sleep(6000);
	 }catch (Exception e){
	 	e.printStackTrace();
	 }
	 return null;
 }

执行结果:

[scheduling-3] task4 2024-05-11 16:54:01
[scheduling-3] task4 2024-05-11 16:54:07
[scheduling-5] task4 2024-05-11 16:54:13
[scheduling-5] task4 2024-05-11 16:54:19
[scheduling-1] task4 2024-05-11 16:54:25
[scheduling-1] task4 2024-05-11 16:54:31
[scheduling-1] task4 2024-05-11 16:54:37
[scheduling-1] task4 2024-05-11 16:54:43
[scheduling-1] task4 2024-05-11 16:54:49
[scheduling-1] task4 2024-05-11 16:54:55
[scheduling-1] task4 2024-05-11 16:55:01  

结论: fixedRate则是基于上一次任务开始的时间来计算下一次任务的开始时间。
如果执行时长超过间隔,那么会在任务执行结束后立刻执行下一次,除非用 @Async
第一次执行延迟了2秒钟,那么下面几次都会立刻执行,直到把这延迟的2秒钟追回来

Async

使用@Async可以避免这个问题
cron也同理

@Async异步方法默认使用Spring创建ThreadPoolTaskExecutor
默认核心线程数:8
最大线程数:Integet.MAX_VALUE
队列使用LinkedBlockingQueue,容量是:Integet.MAX_VALUE
空闲线程保留时间:60s
线程池拒绝策略:AbortPolicy

@Scheduled(fixedRate = 4000)
@Async
public void task4() throws InterruptedException {
    String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
    log.info("task4 " + date);
    Thread.sleep(6000);
}

执行结果:

[task-1] task4 2024-05-13 16:39:29
[task-2] task4 2024-05-13 16:39:33
[task-3] task4 2024-05-13 16:39:37
[task-4] task4 2024-05-13 16:39:41
[task-5] task4 2024-05-13 16:39:45
[task-6] task4 2024-05-13 16:39:49
[task-7] task4 2024-05-13 16:39:53
[task-8] task4 2024-05-13 16:39:57
[task-1] task4 2024-05-13 16:40:01
[task-2] task4 2024-05-13 16:40:05
[task-3] task4 2024-05-13 16:40:09
[task-4] task4 2024-05-13 16:40:13
[task-5] task4 2024-05-13 16:40:17
[task-6] task4 2024-05-13 16:40:21
[task-7] task4 2024-05-13 16:40:25
[task-8] task4 2024-05-13 16:40:29

结论:

@Asyc 需要三步配置,否则没有效果

在@SpringBootApplication启动类 添加注解@EnableAsync
异步方法使用注解@Async ,返回值为void或者Future
切记一点 ,异步方法和调用方法一定要写在 不同的类中 ,如果写在一个类中, 是没有效果的

fixedDelay

 @Scheduled(fixedDelay = 4000)
 public String ssss(){
	  String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
           System.out.println("task4 " + date);
	 try {
	 	Thread.sleep(6000);
	 }catch (Exception e){
	 	e.printStackTrace();
	 }
	 return null;
 }

执行结果:

[scheduling-4] task2 2024-05-11 16:54:01
[scheduling-4] task2 2024-05-11 16:54:11
[scheduling-2] task2 2024-05-11 16:54:21
[scheduling-5] task2 2024-05-11 16:54:31
[scheduling-3] task2 2024-05-11 16:54:41
[scheduling-5] task2 2024-05-11 16:54:51
[scheduling-2] task2 2024-05-11 16:55:01

结论:
是基于上一次任务结束的时间来计算下一次任务的开始时间