Spring Boot @Scheduled未启用多线程运行问题咨询
你遇到的问题核心在于Spring默认不允许同一个@Scheduled任务并发执行,即使你配置了多线程的任务调度池。让我拆解一下原因和解决方案:
问题原因
你的线程池配置是正确的,但@Scheduled(fixedRate = 2000)的默认行为是:对于同一个任务方法,调度器会等待上一次执行完成后,才会触发下一次执行(即使fixedRate的时间间隔已经到了)。这是因为Spring的ScheduledAnnotationBeanPostProcessor默认将allowConcurrentExecution设为false,它会保证同一个任务的多次执行不会重叠,所有触发请求会排队等待上一次执行结束。
所以你的任务每次执行要睡10秒,fixedRate设为2秒,当第2秒到的时候,调度器发现上一次任务还在运行,就会把这次触发请求放入队列,直到10秒后上一次任务结束,才会立刻执行下一次——这时候线程池里的my-sched-pool-1刚好空闲,所以会复用这个线程,导致你看到的日志全是同一个线程。
解决方案
如果你希望同一个任务可以并发执行(也就是上一次还在运行时,下一次触发就用新线程执行),可以通过以下两种方式实现:
方案1:使用@Async实现异步执行
给你的定时任务方法加上@Async注解,同时确保你已经开启异步支持:
首先,在启动类或配置类上添加@EnableAsync:
@SpringBootApplication @EnableScheduling @EnableAsync public class YourApplication { public static void main(String[] args) { SpringApplication.run(YourApplication.class, args); } }
然后修改定时任务方法:
@Async @Scheduled(fixedRateString = "2000" ) public void testaMethod() { log.debug("here is the message"); try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } }
这样每次调度都会把任务提交到异步线程池,即使上一次任务还在运行,新的任务也会用新的线程执行,你就能看到不同的线程日志了。
方案2:开启任务的并发执行允许
通过配置ScheduledAnnotationBeanPostProcessor,将allowConcurrentExecution设为true,这样同一个任务的多次执行可以并发:
在你的SchedulerConfig里添加这个Bean:
@Configuration @EnableScheduling public class SchedulerConfig implements SchedulingConfigurer { private final int POOL_SIZE = 10; @Override public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) { ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler(); threadPoolTaskScheduler.setPoolSize(POOL_SIZE); threadPoolTaskScheduler.setThreadNamePrefix("my-sched-pool-"); threadPoolTaskScheduler.initialize(); scheduledTaskRegistrar.setTaskScheduler(threadPoolTaskScheduler); } @Bean public ScheduledAnnotationBeanPostProcessor scheduledAnnotationBeanPostProcessor() { ScheduledAnnotationBeanPostProcessor processor = new ScheduledAnnotationBeanPostProcessor(); processor.setAllowConcurrentExecution(true); return processor; } }
这样配置后,当fixedRate的触发时间到了,即使上一次任务还在运行,调度器会直接从线程池里拿空闲线程执行新的任务,你就能看到多个不同的线程日志了。
补充说明
- 方案1的异步线程池和调度线程池是分开的,你可以单独配置异步线程池的参数;方案2则直接使用你配置的调度线程池处理并发任务。
- 注意并发执行可能带来的线程安全问题,如果你的任务涉及共享资源,一定要做好同步处理。
内容的提问来源于stack exchange,提问作者brain storm

