Spring questions about @ Scheduled restrictions

  • 2021-11-29 23:47:28
  • OfStack

Directory Spring @ Scheduled Limit Spring Multiple Timing Tasks @ Scheduled Execution Blocking 1. Problem Description 2. Scenario Reproduction 3. Solution 4. Summary

Spring @ Scheduled restrictions

@ Scheduled has a restriction of 1. After all, it is not quartz, but a simple timing. Compared with jdk Timer, it has joined the thread pool and

@Scheduled Year timing is not supported @Scheduled The letters W L are not supported

There is no way. If you have to use it, you can only give up annotations and use XML

Spring Multiple Timing Task @ Scheduled Execution Blocking

1. Description of the problem

Recently, a problem was found in the project, and it is planned to perform a scheduled task at 4:40 am every day, using the annotation method: @ Scheduled (cron = "0 40 4 * *?" ), the cron expression is obviously no problem, but this timed task is always not executed on time, sometimes it has to wait until after 8 o'clock, and sometimes it doesn't execute until after 9 o'clock. Later, after checking, it turns out that this timing mode is executed by single thread by default. It happens that I have multiple timing tasks here, and one of them is time-consuming before 4:40, which leads to the 4:40 task can only be triggered after the previous task is completed, so I have to manually set the timing task to multi-thread mode.

2. Scene reappearance

Project Description: Use Springboot for development

Set two timed tasks, execute them once every 5s, and print out their execution status

The code is as follows:


@Component
@Log4j2
public class ScheduledTask {
    @Scheduled(cron = "0/5 * * * * ?")
    public void task1() throws InterruptedException {
        log.info("I am task11111111, current thread: {}", Thread.currentThread());
        while (true) {
            // Simulate time-consuming tasks, blocking 10s
            Thread.sleep(10000);
            break;
        }
    }
    @Scheduled(cron = "0/5 * * * * ?")
    public void task2() {
        log.info("I am task22222222, current thread: {}", Thread.currentThread());
    }
}

The implementation results are as follows:

2019-04-24 17:11:15.008 INFO 16868 --- [ scheduling-1] com.example.demo.task.ScheduledTask : I am task22222222, current thread: Thread[scheduling-1,5,main]
2019-04-24 17:11:15.009 INFO 16868 --- [ scheduling-1] com.example.demo.task.ScheduledTask : I am task11111111, current thread: Thread[scheduling-1,5,main]
2019-04-24 17:11:25.009 INFO 16868 --- [ scheduling-1] com.example.demo.task.ScheduledTask : I am task22222222, current thread: Thread[scheduling-1,5,main]
2019-04-24 17:11:30.002 INFO 16868 --- [ scheduling-1] com.example.demo.task.ScheduledTask : I am task22222222, current thread: Thread[scheduling-1,5,main]
2019-04-24 17:11:30.003 INFO 16868 --- [ scheduling-1] com.example.demo.task.ScheduledTask : I am task11111111, current thread: Thread[scheduling-1,5,main]
2019-04-24 17:11:40.004 INFO 16868 --- [ scheduling-1] com.example.demo.task.ScheduledTask : I am task22222222, current thread: Thread[scheduling-1,5,main]

It can be seen from the results that task1 and task2 are executed by the same thread Thread [scheduling-1, 5, main], that is, the timing task uses a single thread by default, and because task1 blocks 10s, the timing task 10s, which should have been executed once by 5s, is executed once.

3. Solutions

There are many solutions on the Internet, two of which are listed below

Scenario 1: Using @ Async annotations to implement asynchronous tasks

This method is relatively simple. Add @ Async annotation to the timed task. Note: It will take effect only if the startup class cooperates with @ EnableAsync

The code is as follows:


@Component
@Log4j2
public class ScheduledTask {
    @Async
    @Scheduled(cron = "0/5 * * * * ?")
    public void task1() throws InterruptedException {
        log.info("I am task11111111, current thread: {}", Thread.currentThread());
        while (true) {
            // Simulate time-consuming tasks, blocking 10s
            Thread.sleep(10000);
            break;
        }
    }
    @Async
    @Scheduled(cron = "0/5 * * * * ?")
    public void task2() {
        log.info("I am task22222222, current thread: {}", Thread.currentThread());
    }
}

Run results:

2019-04-24 17:03:00.024 INFO 2152 --- [ task-1] com.example.demo.task.ScheduledTask : I am task22222222, current thread: Thread[task-1,5,main]
2019-04-24 17:03:00.024 INFO 2152 --- [ task-2] com.example.demo.task.ScheduledTask : I am task11111111, current thread: Thread[task-2,5,main]
2019-04-24 17:03:05.001 INFO 2152 --- [ task-3] com.example.demo.task.ScheduledTask : I am task11111111, current thread: Thread[task-3,5,main]
2019-04-24 17:03:05.001 INFO 2152 --- [ task-4] com.example.demo.task.ScheduledTask : I am task22222222, current thread: Thread[task-4,5,main]
2019-04-24 17:03:10.002 INFO 2152 --- [ task-5] com.example.demo.task.ScheduledTask : I am task22222222, current thread: Thread[task-5,5,main]
2019-04-24 17:03:10.003 INFO 2152 --- [ task-6] com.example.demo.task.ScheduledTask : I am task11111111, current thread: Thread[task-6,5,main]

From the running log, it can be seen that the timing is executed once every 5s, and the threads used for each task are different, that is, multi-threaded timing tasks are implemented, and the task waiting phenomenon will not occur. It is said that the default thread pool size is 100, which is a bit overqualified if there are not many tasks, so I think the second method is better.

Scenario 2: Manually set the thread pool size of timed tasks

The timed task code is partially restored without using @ Async annotation, and the startup code configuration is added:


@Configuration
public class AppConfig implements SchedulingConfigurer {
    @Bean
    public Executor taskExecutor() {
     // Specify the number of timed task threads, which can be adjusted according to requirements 
        return Executors.newScheduledThreadPool(3);
    }
    @Override
    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
        scheduledTaskRegistrar.setScheduler(taskExecutor());
    }
}

The running results are as follows:

2019-04-24 17:26:15.008 INFO 2164 --- [pool-1-thread-2] com.example.demo.task.ScheduledTask : I am task22222222, current thread: Thread[pool-1-thread-2,5,main]
2019-04-24 17:26:15.008 INFO 2164 --- [pool-1-thread-1] com.example.demo.task.ScheduledTask : I am task11111111, current thread: Thread[pool-1-thread-1,5,main]
2019-04-24 17:26:20.002 INFO 2164 --- [pool-1-thread-2] com.example.demo.task.ScheduledTask : I am task22222222, current thread: Thread[pool-1-thread-2,5,main]
2019-04-24 17:26:25.001 INFO 2164 --- [pool-1-thread-2] com.example.demo.task.ScheduledTask : I am task22222222, current thread: Thread[pool-1-thread-2,5,main]
2019-04-24 17:26:30.001 INFO 2164 --- [pool-1-thread-1] com.example.demo.task.ScheduledTask : I am task11111111, current thread: Thread[pool-1-thread-1,5,main]
2019-04-24 17:26:30.001 INFO 2164 --- [pool-1-thread-3] com.example.demo.task.ScheduledTask : I am task22222222, current thread: Thread[pool-1-thread-3,5,main]
2019-04-24 17:26:35.001 INFO 2164 --- [pool-1-thread-3] com.example.demo.task.ScheduledTask : I am task22222222, current thread: Thread[pool-1-thread-3,5,main]

It can be seen from the results that the second method also realizes multithreaded task scheduling.

4. Summary

Both methods have their own advantages and disadvantages:

比较 方案1 方案2
优点 注解方式使用简单,代码量少 配置灵活,线程数可控
缺点 线程数不可控,可能存在资源浪费 需要增加编码

Leave a pit. From the log, @ Async mode is also asynchronous for the same task, that is, task1 will be executed once every 5s, but Mode 2 seems to be ineffective for the same task. When task1 is executed, it will not be triggered until the last execution ends, but it is not executed once every 5s. About this phenomenon, think about it next time …


Related articles: