本文主要是介绍SpringBoot中提供@Scheduler和@Async的不同使用场景,以及关于其线程池的使用
@Scheduler
表示一个任务调度注解,用于配置定时任务,默认启用调度器的线程池大小为1。
由于SpringBoot对于Scheduler注解默认的线程池只有一个线程,如果多个方法添加该注解时,多个任务就是进入一个延时队列,一个一个执行。
@Async
表示任务异步执行,该注解标识的方法,会从使用线程池中新线程执行。SpringBoot默认执行器的线程池大小为100。
此注解会将任务放到一个线程异步中执行,不会阻塞主线程。一般用于一些比较耗时并且不同考虑返回值的操作方法上。
开启两个注解功能的支持
在启动类上添加注解 或者配置类中
@EnableAsync
@EnableScheduling
..........
@Scheduled(fixedDelay = 1000)
@Async
public void executeUpdateYqTask() {
System.out.println(Thread.currentThread().getName() + " >>> task one " + format.format(new Date()));
}
@Scheduled(fixedDelay = 1000)
@Async
public void executeRepaymentTask() throws InterruptedException {
System.out.println(Thread.currentThread().getName() + " >>> task two " + format.format(new Date()));
Thread.sleep(5000);
}
...........
配置调度器的多线程(TaskScheduler)
如果使用默认线程为1的调度器线程池的话,如果当有多个定时任务时,可能会进行入一个执行队列,一个一个执行,如果有其中一个任务执行失败,可能其他任务也无法正常执行。
自定配置调度器线程池
@Bean(name = "scheduledExecutorService")
protected ScheduledExecutorService scheduledExecutorService() {
// 父类为ThreadPoolExecutor 最后会使用ThreadPoolTaskExecutor
return new ScheduledThreadPoolExecutor(corePoolSize,
new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build()) {
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
Threads.printException(r, t);
}
};
}
此时我们就可以解决多个任务调度器阻塞的问题,也可以配置ThreadPoolTaskScheduler实例。
为什么还需要Async(TaskExecutor)
我们通过自定义调度器线程池解决了多个任务阻塞的问题可以并发执行,但是单个任务仍然是同步执行(当执行时间大于我们间隔时间时)
这时候需要配合使用async注解,每次执行定时任务都会新开一个线程,非阻塞异步执行。Scheduler负责任务的调度,Async负责指定对应的线程池(Executor)执行任务。
Async未指定线程池时,默认使用SimpleAsyncTaskExecutor,该线程池没有线程数的限制,可能会存在内存泄漏的风险。
需要自定义线程确保执行的核心线程数,SpringBoot容器会自动需要TaskExecutor的实现类Bean进行初始化。
@Bean(name = "threadPoolTaskExecutor")
public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setMaxPoolSize(maxPoolSize);
executor.setCorePoolSize(corePoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setKeepAliveSeconds(keepAliveSeconds);
executor.setThreadNamePrefix("task-schedule");
// 线程池对拒绝任务(无线程可用)的处理策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
return executor;
}
写在最后
Scheduler注解线程池只有一个线程,多任务时串行。Scheduler+Async注解多任务间并行。(多任务间非阻塞异步执行)主要是带领大家初步了解SpringBoot中定时任务和异步任务使用两种不同的线程池,希望能够帮助大家,如果有问题或纰漏,欢迎指出。
生活大于一切,爱生活,爱运动。