how can you configure spring to execute overlapping fixedRate tasks?

i am trying to execute a task at a fixed rate using the @Scheduled annotation in java spring. however, it seems that by default spring will not execute a fixedRate task at a fixed rate if the task is slower than the rate. is there some setting i can add to my spring configuration to change this behavior?

example :

@Service
public class MyTask{
    @Scheduled(fixedRate = 1000)
    public void doIt(){
        // this sometimes takes >1000ms, in which case the next execution is late
        ...
    }
}

i have a work-around , but it seems less than ideal. basically, i just replace the default single-thread executor with a thread pool, then i have a scheduled method call an async method since the @Async annotation allows concurrent executions:

@Service
public class MyTask{
    @Async
    public void doIt(){
        // this sometimes takes >1000ms, but the next execution is on time
        ...
    }
}

@Service
public class MyTaskScheduler{
    ...
    @Scheduled(fixedRate = 1000)
    public void doIt(){
        myTask.doIt();
    }
}

@Configuration
@EnableScheduling
@EnableAsync
public class MySpringJavaConfig{
    @Bean(destroyMethod = "shutdown")
    public Executor taskScheduler() {
        return Executors.newScheduledThreadPool(5);
    }
}

boring details of my real-world scenario : in my production code i have a task that takes between 10ms and 10 minutes depending on the current workload. ideally, i would like to capture a new thread from the pool every 1000ms so that the number of concurrent threads increases with the workload. obviously i have an upper limit on threads in place (among other controls) to keep things from getting out of hand.


The TaskScheduler API (which backs some of the Spring Scheduling behavior) seems to be defined to prevent the behavior you are requesting

Schedule the given Runnable , invoking it at the specified execution time and subsequently with the given period.

Parameters

  • period the interval between successive executions of the task (in milliseconds)
  • subsequently and successive seem to indicate that the next execution will only occur after the current execution is complete.

    What's more, ScheduledExecutorService#scheduleAtFixedRate(..) (which the built-in TaskScheduler implementations use) also says

    If any execution of this task takes longer than its period, then subsequent executions may start late, but will not concurrently execute .

    So there's another layer of the implementation that prevents the behavior you want.

    A possible solution, and one I don't recommend since the API doesn't seem to be built around it, is to define and provide your own TaskScheduler that does run the task concurrently. Look into @EnableScheduling and SchedulingConfigurer for how to register a TaskScheduler .


    the best solution i have found so far is to simply use a delegate to make the method calls async. this is only preferable because it allows me to declare the schedule in the same class as the method which does the work:

    @Service
    public class AsyncRunner {
        @Async
        public void run(Runnable runnable) {
            runnable.run();
        }
    }
    
    @Service
    public class MyTask{
        ...
        @Scheduled(fixedRate = 1000)
        public void scheduleIt(){
            asyncRunner.run(this::doIt);
        }
        public void doIt(){
            // this sometimes takes >1000ms, but the next execution is on time
            ...
        }
    }
    
    链接地址: http://www.djcxy.com/p/85400.html

    上一篇: 为什么编辑需要这么长时间?

    下一篇: 你如何配置spring来执行重叠的fixedRate任务?