原文 springboot中@Scheduled 和@Async的使用_huangyaa729的博客-CSDN博客_@async @scheduled
首先,需要了解@Scheduled 和@Async这俩注解的区别:
@Scheduled 任务调度注解,主要用于配置定时任务;springboot默认的调度器线程池大小为 1。
@Async 任务异步执行注解,主要用于方法上,表示当前方法会使用新线程异步执行;springboot默认执行器线程池大小为100。
具体可参考源码和这两篇博客(里面有些描述感觉有点问题,但不影响核心思想的表达,大家可参考看下):
https://blog.csdn.net/hello__ZC/article/details/102455847
https://blog.csdn.net/weixin_34319374/article/details/88811716
所以,如果在使用springboot定时器时,如果有多个定时任务时,在使用默认的调度器配置,就会出现排队现象,因为同时只能有一个任务在执行,这个时候当一个任务挂死,那后面的定时任务就不能有效执行了;
解决办法就是自定义调度器,有两种方式:
方法一:
- @Bean
- public TaskScheduler scheduledExecutorService() {
- ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
- scheduler.setPoolSize(10);
- scheduler.setThreadNamePrefix("scheduled-thread-");
- //设置线程池关闭的时候等待所有任务都完成再继续销毁其他的Bean
- scheduler.setWaitForTasksToCompleteOnShutdown(true);
- //设置线程池中任务的等待时间,如果超过这个时候还没有销毁就强制销毁,以确保应用最后能够被关闭,而不是阻塞住
- scheduler.setAwaitTerminationSeconds(60);
- //这里采用了CallerRunsPolicy策略,当线程池没有处理能力的时候,该策略会直接在 execute 方法的调用线程中运行被拒绝的任务;如果执行程序已关闭,则会丢弃该任务
- scheduler.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
- return scheduler;
- }
方法二:
- @Configuration
- public class ScheduledConfig implements SchedulingConfigurer {
-
- public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
- taskRegistrar.setScheduler(setExecutor());
- }
-
- @Bean(destroyMethod="shutdown")
- public Executor setExecutor(){
- return Executors.newScheduledThreadPool(10); // 10个线程来处理。
- }
- }
上述自定义调度器的方式,会有一个问题:当有足够的空余线程时,多任务时并行执行,但是同一定时任务仍会同步执行(当定时任务的执行时间大于每次执行的时间间隔时即可发现);
配合@Async 注解使用,这样在每次执行定时任务时就新开一个线程,异步非阻塞运行;同时使用这两个注解的效果,相当于@Scheduled仅仅负责调度,而@Async指定的executor负责任务执行,不再使用调度器中的执行器来执行任务(由实际测试结果来猜测的,并没有找到对应的源码逻辑,待后续补充)。
自定义执行器配置如下:
- @Bean("taskExecutor")
- public ThreadPoolTaskExecutor taskExecutor(){
- ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
- executor.setCorePoolSize(corePoolSize);
- executor.setMaxPoolSize(maxPoolSize);
- executor.setQueueCapacity(queueCapacity);
- executor.setKeepAliveSeconds(keepAliveTime);
- executor.setThreadNamePrefix(threadNamePrefix);
-
- // 线程池对拒绝任务的处理策略
- executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
- // 初始化
- executor.initialize();
- return executor;
- }
其余注意事项,可参考如下两份博客:
https://blog.csdn.net/hello__ZC/article/details/102455847
https://blog.csdn.net/dviewer/article/details/78022865