定时任务是一种很常见的应用场景,springboot中的定时任务完全用的spring的那一套,用起来比较简单,需要注意的是线程池配置的那一块
使用 @EnableScheduling 注解就可以开启定时任务
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(SchedulingConfiguration.class)
@Documented
public @interface EnableScheduling {
}
这个注解引用了 SchedulingConfiguration.class 类
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class SchedulingConfiguration {
@Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
return new ScheduledAnnotationBeanPostProcessor();
}
}
这个配置类也很简单,主要定义了 ScheduledAnnotationBeanPostProcessor 类型的ben,这个类的作用就是把标志了
@Scheduled(fixedDelay = 1*1000) 注解的方法收集起来,封装成一个个定时任务,再构造用于执行任务的线程池
@Component
public class ScheduleOneTest {
private static final Logger LOGGER = LoggerFactory.getLogger(ScheduleOneTest.class);
@Scheduled(fixedDelay = 1*1000)
public void doWork(){
LOGGER.info("任务一正在执行");
try {
Thread.sleep( 5 * 60 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
LOGGER.info("任务一执行结束");
}
需要注意的是,默认情况下,spring只构造了包含一个线程的线程池
ScheduledTaskRegistrar类
if (this.taskScheduler == null) {
this.localExecutor = Executors.newSingleThreadScheduledExecutor();
this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);
}
所以如果我们有多个任务需要执行,实际上是不能并发执行的,因为只有一个工作线程
@Component
public class ScheduleTwoTest {
private static final Logger LOGGER = LoggerFactory.getLogger(ScheduleTwoTest.class);
@Scheduled(fixedDelay = 1*1000)
public void doWork(){
LOGGER.info("任务二正在执行");
try {
Thread.sleep( 5 * 60 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
LOGGER.info("任务二执行结束");
}
}
像上面这样,两个一样的任务,同时只能执行一个,必须等一个执行完,另一个才会执行,那么我们如何为spring的定时任务提供包含多个线程的线程池呢?
@Configuration
public class ScheduleConfig implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
// taskRegistrar.setTaskScheduler(poolTaskScheduler());
taskRegistrar.setScheduler(threadPoolTaskScheduler());
}
@Bean(name = "taskScheduler", destroyMethod = "shutdown")
public ScheduledThreadPoolExecutor threadPoolTaskScheduler(){
// ThreadPoolTaskScheduler poolTaskScheduler = new ThreadPoolTaskScheduler();
// poolTaskScheduler.setPoolSize(Runtime.getRuntime().availableProcessors() * 5);
// return poolTaskScheduler;
return new ScheduledThreadPoolExecutor(Runtime.getRuntime().availableProcessors() * 5);
}
}
看上面代码,实现 SchedulingConfigurer 接口,重写 configureTasks 方法,上面注释掉的代码和没注释掉的功能是一样的,需要注意的是,线程池的bean名称需要设置为 taskScheduler
public void setScheduler(Object scheduler) {
Assert.notNull(scheduler, "Scheduler object must not be null");
if (scheduler instanceof TaskScheduler) {
this.taskScheduler = (TaskScheduler) scheduler;
}
else if (scheduler instanceof ScheduledExecutorService) {
this.taskScheduler = new ConcurrentTaskScheduler(((ScheduledExecutorService) scheduler));
}
else {
throw new IllegalArgumentException("Unsupported scheduler type: " + scheduler.getClass());
}
}
这样就可以为spring的定时任务提供指定线程个数的线程池了,至于定时任务的用法,挺简单的,请参考其他资料