Spring @Scheduled fixedDelay未按预期工作问题咨询
嘿,我来帮你捋清楚这个问题——你的fixedDelay行为不符合预期,核心原因其实是Spring Batch默认的SimpleJobLauncher是异步执行的!
为什么会这样?
看你的代码,当调用jobLauncher.run(job, jobParameters)时,这个方法并不会等待Job真正执行完成,而是把Job提交给任务执行器后就立刻返回了。这就导致@Scheduled(fixedDelay)的计时起点是runJob()方法结束的时间(也就是Job刚被提交的那一刻),而不是Job实际执行完毕的时间。
你的日志也完美印证了这点:
第一次
sendMailJob在12:48:24.721被启动,此时runJob()方法已经执行完并返回了,Spring Scheduler就开始掐表算30秒,到点就再次调用runJob(),再次提交新的Job。所以两次Job的开始时间间隔刚好是30秒,完全不是你想要的“上一次Job结束到下一次开始间隔30秒”。
怎么解决?
要让fixedDelay基于Job实际完成时间来计算,我们需要让jobLauncher.run()等待Job执行完成,有两种常用方式:
方式1:配置同步的JobLauncher(推荐)
修改你的JobLauncher配置,让它使用同步的任务执行器,这样run()方法会阻塞到Job完全执行完毕才返回:
@Bean public JobLauncher jobLauncher(JobRepository jobRepository) throws Exception { SimpleJobLauncher jobLauncher = new SimpleJobLauncher(); jobLauncher.setJobRepository(jobRepository); // 替换成同步执行器,确保run方法等待Job完成 jobLauncher.setTaskExecutor(new SyncTaskExecutor()); jobLauncher.afterPropertiesSet(); return jobLauncher; }
这样配置后,@Scheduled(fixedDelay)的计时就会从Job真正结束的时间开始,完全符合你的预期。
方式2:在代码里手动等待Job完成
如果不想修改全局的JobLauncher配置,也可以在runJob()方法里手动轮询等待Job执行结束:
@Scheduled(fixedDelay=30000) public void runJob() { try { JobParameters jobParameters = new JobParametersBuilder() .addLong("time", System.currentTimeMillis()) .toJobParameters(); JobExecution execution = jobLauncher.run(job, jobParameters); // 轮询等待Job执行完成 while (execution.isRunning()) { Thread.sleep(100); // 每隔100ms检查一次 } LOGGER.info(execution.getExitStatus()); } catch (Exception e) { LOGGER.error("Scheduler ERROR :: Error occurred during Job run: {}", e); } }
不过这种方式相对繁琐,不如第一种配置同步JobLauncher优雅。
小建议
另外,你的异常处理代码有点冗余,不需要嵌套抛出SystemException再捕获,直接在catch块里打印日志就好,简化后代码会更清晰易读。
内容的提问来源于stack exchange,提问作者reshma begum

