Problem description

Springboot timed task with everyone should be able to use, add two annotations, add configuration can run. But if you’re just at the application level, there are a lot of inherent problems that can go unnoticed during development. Without further ado, let me begin by describing, in a way of extreme hyperbole, a problem encountered.

@Component
public class ScheduleTest {
    @Scheduled(initialDelay = 1000,fixedRate = 2*1000)
    public void test_a(){
        System.out.println("123");
    }

    @Scheduled(initialDelay = 2*1000,fixedRate = 2*1000)
    public void test_b() {while (true){
            try {
                Thread.sleep(2*1000);
                System.out.println("456"); } catch (InterruptedException e) { e.printStackTrace(); }}}}Copy the code

The code above is two timed tasks in a project. Test_a is the normal method and test_b is the exception method. To highlight the exception, I made an endless loop.

What happens when you run with the default scheduled task configuration in this case? Just try it, the timed task is always looped in method B, method A never executes!!


Question why

The default thread pool for Scheduled tasks is only one thread. Therefore, if one of the Scheduled tasks has an exception such as a delay or an infinite loop, other Scheduled tasks may be affected.

The solution

Since the problem lies in the number of thread pools, it is a good idea to configure thread pools so that tasks do not interfere with each other.

Scheme 1 is executed asynchronously

    @Scheduled(initialDelay = 1000,fixedRate = 2*1000)
    @Async
    public void test_a(){
        System.out.println("123");
    }

    @Scheduled(initialDelay = 2*1000,fixedRate = 2*1000)
    @Async
    public void test_b() {while (true){
            try {
                Thread.sleep(2*1000);
                System.out.println("456"); } catch (InterruptedException e) { e.printStackTrace(); }}}Copy the code

Since one task stuck in a single thread affects other tasks, execute that task asynchronously and the problem is solved.

Solution 2 Customize the number of thread pools for scheduled tasks

@Configuration
public class GlobalConfiguration {

    @Bean
    public TaskScheduler schedule() {returnnew ConcurrentTaskScheduler(new ScheduledThreadPoolExecutor(2)); }}Copy the code

Since single threads interfere with each other, it is possible to allocate enough threads to run them separately.

Compare the two schemes

Both solutions can solve the problem of interference between tasks, but the appropriate one needs to be selected according to the actual situation. Let’s analyze the code with the dead-loop above.

If an infinite loop does occur in a timed task, using asynchronous execution can be disastrous. Because in the timed task thread, after each task is completed, it calculates the next time and adds another task to the asynchronous thread pool. Tasks added to the asynchronous thread pool continue to consume thread resources because of an infinite loop. Over time, all thread resources in the asynchronous thread pool are occupied by an infinite loop of tasks, causing all other services to block.

It is better to use a custom timed task thread pool, because only after the task has completed will time be counted to execute the next task. Although the dead-loop task is always executed, it occupies the resources of one thread at most, so it does not have a wider impact.


Returns the directory