SpringBoot可以通过@Scheduled注解启用定时任务。
1.简单使用
第一要在SpringBoot启动类上面开启Schedule,即加上@EnableScheduling注解,且@Scheduled注解需要在被spring管理的bean中才生效,即需要使用@Component注解。
被@Scheduled修饰的方法将会根据配置定时调用该方法。例如cron或者固定间隔。
@Scheduled(cron = "0/3 * * * * ?")
// @Scheduled(fixedDelay = 3 * 1000)
// @Scheduled(fixedRate = 3 * 1000)
private void test(){
System.out.println("test");
}
2.不同配置的差别
| 配置方式 | 配置含义 | 任务执行时间过久的差别 |
|---|---|---|
| cron | 根据配置的cron表达式进行调用 | 任务执行中,再次到了触发时间,不执行,等任务执行完了之后的下一次触发,再执行 |
| fixedDelay | 方法执行结束之后,在进行计时,单位毫秒 | 不存在同时触发 |
| fixedRate | 方法执行结束开始时进行计时,单位毫秒 | 阻塞等待,上次执行完立马再次执行,不过貌似不会阻塞过多任务 |
@Component
public class ScheduledTest {
@Resource
private ScheduledTaskHolder scheduledTaskHolder;
@Scheduled(cron = "0/3 * * * * ?")
// @Scheduled(fixedDelay = 3 * 1000)
// @Scheduled(fixedRate = 3 * 1000)
private void test() throws InterruptedException{
System.out.println("Start=>" + LocalDateTime.now());
// 获取任务数 方式存疑
System.out.println(scheduledTaskHolder.getScheduledTasks().size());
Thread.sleep(5000);
System.out.println("end=>" + LocalDateTime.now());
}
}
不论哪种方式 任务数貌似都是1,不过获取任务数的方式不保证正确。
3. 开启多线程
默认只有一个线程去执行任务,如果有多个被@Scheduled修饰的方法,同时只有一个在运行。
如果有多个任务一般都不会想让他们去抢同一个线程去执行,那么就需要开启多线程模式。
3.1 @EnableAsync + @Async
网上最简单粗暴的方式是给类加上@EnableAsync并且给方法加上@Async。
@Component
@EnableAsync
public class ScheduledTest {
@Scheduled(cron = "0/3 * * * * ?")
@Async
protected void test() throws InterruptedException{
System.out.println("Start=>" + LocalDateTime.now());
Thread.sleep(5000);
System.out.println("end=>" + LocalDateTime.now());
}
}
细心的小伙伴应该发现test方法从private变成protected了,由于@Async是通过动态代理的方式来将该任务提交到线程池中去执行的。而且这个线程池貌似没有限制核心线程数(我没看具体实现)。而Spring的动态代理通过操控字节码来生成子类,以继承的方式实现的,所以test方法不能是private的。
这个方法并没有改变@Schduled本身的线程池,不同的定时任务依旧是在一个线程中执行的,只不过变成了在这个线程中提交到另一个线程池,达到了不阻塞的结果。所以上面分析的任务执行时间过长的分析也就失效了,由于对@Scheduled本身来说,任务瞬间就执行结束了。
也就是说,你的同一个任务可能同时有多个在执行,且如果不是所有的@Scheduled方法都被@Async修饰,那么那个没被修饰的方法,依旧会阻塞别的被@Async修饰的方法的提交。
3.2 代码配置
@Configuration
public class ScheduledConfig implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(Executors.newScheduledThreadPool(10));
}
}
3.3配置文件
spring:
task:
scheduling:
pool:
size: 10
我觉得用后面两个就好了,@Async毕竟不是专门给@Scheduled用的,虽然简单粗暴。而后两种方式线程数要看情况设置,具体的自己看着来吧~


