Spring Scheduled fixedRate 工作异常问题咨询
问题分析与解决方案
先帮你拆解下问题根源,再给出具体的排查和解决办法:
为什么任务没按预期每秒触发?
你对@Scheduled(fixedRate = 1000)的工作逻辑存在误解,再加上Spring默认调度线程池的限制,才导致了这个问题:
fixedRate的规则是以上一次任务开始执行的时间为基准,每隔指定时长尝试触发下一次任务。- 但Spring默认的调度线程池是单线程的——如果当前任务还在运行(你的任务里
Thread.sleep(5500L)让执行时长达到5.5秒),下一次触发的任务会被塞进队列等待,直到当前任务执行完毕才会启动。所以实际你看到的是每5.5秒左右执行一次,而非每秒一次。
排查与解决步骤
1. 验证线程池阻塞问题
从你代码的输出就能验证这点:所有任务的Thread.currentThread().getId()都是同一个,而且下一次的START THREAD一定会等上一次的END THREAD打印后才出现,这说明所有任务都在同一个线程里排队执行。
2. 配置多线程调度池
要实现真正的每秒触发(哪怕任务执行时长超过1秒),你需要自定义一个多线程的任务调度池,让新任务可以在独立线程中启动,不会被正在运行的任务阻塞。
添加下面的配置类即可:
import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.SchedulingConfigurer; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.scheduling.config.ScheduledTaskRegistrar; @Configuration public class SchedulerConfig implements SchedulingConfigurer { @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); // 根据你的需求设置线程池大小,比如5个线程足够应对你的场景 scheduler.setPoolSize(5); scheduler.setThreadNamePrefix("scheduled-task-"); scheduler.initialize(); taskRegistrar.setTaskScheduler(scheduler); } }
3. 验证效果
配置完成后重新运行代码,你会看到每隔1秒就有新线程启动任务(Thread ID会不一样),即使前一个任务还在sleep中。输出会类似:
START THREAD 123 START THREAD 124 // 1秒后触发 START THREAD 125 // 又1秒后触发 END THREAD 123 // 5.5秒后第一个任务结束 START THREAD 126 // 第6秒触发 ...
4. 额外提示:区分fixedRate与fixedDelay
如果你的需求其实是上一次任务结束后再过1秒执行,那应该用fixedDelay = 1000而非fixedRate。但根据你的描述,你需要的是每秒触发一次,所以多线程池才是正确的解决方案。
内容的提问来源于stack exchange,提问作者Dimitar Spasovski




