结论
@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
结论:
是基于上一次任务结束的时间来计算下一次任务的开始时间