Spring Schedule
一般简单的定时任务需要使用Spring的Scheduled执行比较简单,但是如果不了解原理,可能会入坑。下面简单的从源码出发介绍下;有兴趣可以在标识处打个断点调试下。
概论
Spring的Scheduled内部实现就是将方法体构造成Runnable,根据注解中不同配置构造,在内部调用JUC的ScheduledExecutorService的API去实现,这里有个问题就是ScheduledThreadPoolExecutor的线程数配置几个,如果配置的不对,对时间敏感性的需求是有问题的,直接表现为定时任务没有按指定时间运行;根本原因在于默认配置,内部只有一个线程执行。
解决方式按需求配置多个线程执行,配置方式有两种:
第一种 配置Bean
@Bean("taskScheduler")
public TaskScheduler taskScheduler() {
return new ConcurrentTaskScheduler(Executors.newScheduledThreadPool(5));
}
第二种 实现SchedulingConfigurer
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
ConcurrentTaskScheduler concurrentTaskScheduler = new ConcurrentTaskScheduler(Executors.newScheduledThreadPool(5));
taskRegistrar.setTaskScheduler(concurrentTaskScheduler);
}
如果两种都存在,根据下面源码分析知道第二种生效
ScheduledAnnotationBeanPostProcessor
通过EnableScheduling找到SchedulingConfiguration,定位到ScheduledAnnotationBeanPostProcessor
注:配置类 项目里的定时任务配置QuartzConfig, 后面有用
下面代码已经删除无关逻辑, 基于Spring 5.12
@Slf4j
@Configuration
@EnableScheduling
public class QuartzConfig implements SchedulingConfigurer {
//配置方式1
// @Bean("taskScheduler") //name=taskScheduler 必须?
// public TaskScheduler taskScheduler() {
// return new ConcurrentTaskScheduler(Executors.newScheduledThreadPool(5));
// }
/**
* 配置方式2
* @param taskRegistrar the registrar to be configured.
*/
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
ConcurrentTaskScheduler concurrentTaskScheduler = new ConcurrentTaskScheduler(Executors.newScheduledThreadPool(5));
taskRegistrar.setTaskScheduler(concurrentTaskScheduler);
//注释这个原理是一样的
//taskRegistrar.setScheduler(Executors.newScheduledThreadPool(5));
}
@Scheduled(fixedRate = 1000 * 20)
public void scheduledTaskA20() {
log.error("任务A每20秒执行一次");
try {
TimeUnit.SECONDS.sleep(20);
} catch (InterruptedException e) {
log.error("InterruptedException A");
}
}
@Scheduled(fixedRate = 1000 * 10)
public void scheduledTaskB10() {
log.error("任务B每10秒执行一次");
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {