Helidon 3.2.1中@Scheduled的concurrentExecution属性行为疑问
问题原因与解决办法
问题根源
你设置的concurrentExecution = false未生效,核心原因是:Helidon的@Scheduled注解的并发控制仅针对被注解的方法本身(即stuckThreadShooter方法),而你的方法只是把耗时的run()逻辑提交到了自定义线程池,方法本身瞬间就执行完毕——调度器会判定当前任务已结束,自然会按20秒间隔准时触发下一次任务,完全不会理会线程池内的任务是否还在运行。
解决方案
方案1:移除自定义线程池,直接在注解方法内执行耗时逻辑
将耗时操作直接放在stuckThreadShooter方法中,此时concurrentExecution = false会生效,调度器会等待当前任务执行完成后,再计算下一次触发时间。
修改后的代码:
@Scheduled(value= "0/20 * * * * ?", concurrentExecution = false) public void stuckThreadShooter() { System.out.println("Starting fixed rate execution at" + new Date().toString()); System.out.println("Starting run for thread: " + Thread.currentThread().getName()+ " "+ new Date().toString()); run(); } private static void run(){ try { Thread.sleep(25000); System.out.println("Completed run for thread: " + Thread.currentThread().getName()+ " "+ new Date().toString()); } catch (InterruptedException e) { e.printStackTrace(); } }
对应的输出会变为:
Starting fixed rate execution atTue Jun 13 15:54:20 IST 2023 Starting run for thread: helidon-scheduler-1 Tue Jun 13 15:54:20 IST 2023 Completed run for thread: helidon-scheduler-1 Tue Jun 13 15:54:45 IST 2023 Starting fixed rate execution atTue Jun 13 15:54:45 IST 2023 Starting run for thread: helidon-scheduler-1 Tue Jun 13 15:54:45 IST 2023 Completed run for thread: helidon-scheduler-1 Tue Jun 13 15:55:10 IST 2023
可以看到,下一次任务会等上一次完全执行完毕后才触发。
方案2:保留自定义线程池,自行控制任务并发
如果必须使用自定义线程池,需自行添加并发控制逻辑,比如用原子标记确保上一个线程池任务完成后,才允许执行下一次任务提交。
示例代码:
private ExecutorService executorService = Executors.newFixedThreadPool(1); private final AtomicBoolean isTaskRunning = new AtomicBoolean(false); @Scheduled(value= "0/20 * * * * ?", concurrentExecution = false) public void stuckThreadShooter() { // 检查是否有任务在运行,有则直接跳过本次触发 if (!isTaskRunning.compareAndSet(false, true)) { System.out.println("Previous task still running, skip this trigger at " + new Date().toString()); return; } System.out.println("Starting fixed rate execution at" + new Date().toString()); executorService.execute(()->{ try { System.out.println("Starting run for thread: " + Thread.currentThread().getName()+ " "+ new Date().toString()); run(); } finally { // 任务完成后重置标记 isTaskRunning.set(false); } }); } private static void run(){ try { Thread.sleep(25000); System.out.println("Completed run for thread: " + Thread.currentThread().getName()+ " "+ new Date().toString()); } catch (InterruptedException e) { e.printStackTrace(); } }
这种情况下,当调度器触发下一次任务时,会检查线程池内的任务状态,若未完成则跳过本次触发,直到上一个任务结束后才会处理下一次调度。
内容的提问来源于stack exchange,提问作者Asif Iqbal
相关产品推荐
相关产品推荐

