@Scheduled scheduled tasks

Posted by thepeccavi on Mon, 14 Feb 2022 11:29:42 +0100

In the development, we sometimes encounter the demand of regular push, such as regular push, regular email and so on. SpringBoot has built-in timing tasks for us. We only need an annotation to enable timing for our use.

Start scheduled task

Add the @ enableshcheduling annotation in the entry class LogApplication. After adding the annotation, SpringBoot has determined that we need to use the scheduled task to complete some business logic. The corresponding configuration file will be added internally corresponding to the original configuration scheduled task.

@SpringBootApplication
@EnableScheduling    +    // Enable scheduled tasks
public class LogApplication {

@Scheduled

Configuring Scheduled tasks is very simple. You only need to add the @ Scheduled annotation to the methods that need to be executed regularly. Note that this class needs to be marked with component annotations, such as @ Componet, so that this class can be injected into the Spring container for management, which is used to indicate that this is a Bean managed by Spring, and @ Scheduled will take effect.
Of course, the derived annotation of @ component: @ Repository, @Service, @Controller will be used instead, which will not be discussed here.

@Service  +
public class DayLogTaskImpl implements DayLogTask {
    @Scheduled(cron = "* * * * * ?")  +
    public void test1() {


Now let's test the code:

@Scheduled(cron = "* * * * * ?")
     public void test1() {
        /**
         * Once per second
         */
        logger.info("scheduler1 implement: " + System.currentTimeMillis());
    }

Operation results:

2022-02-14 13:46:50.000  INFO 2337 --- [   scheduling-1] club.yunzhi.log.task.DayLogTask          : scheduler1 implement: 1644828410000
2022-02-14 13:46:51.003  INFO 2337 --- [   scheduling-1] club.yunzhi.log.task.DayLogTask          : scheduler1 implement: 1644828411003
2022-02-14 13:46:52.000  INFO 2337 --- [   scheduling-1] club.yunzhi.log.task.DayLogTask          : scheduler1 implement: 1644828412000
2022-02-14 13:46:53.000  INFO 2337 --- [   scheduling-1] club.yunzhi.log.task.DayLogTask          : scheduler1 implement: 1644828413000

You can see that the time interval between printing is about 1000 milliseconds, which is executed once per second according to the definition.

@Scheduled annotation parameter description

Cron: this parameter receives a cron expression, which is a string, such asMeans every 5 seconds.
Zone: time zone. The default is the time zone where the server is located, and the receiving type is Java util. TimeZone.
fixedDelay: how long after the last execution is completed. For example:

@Scheduled(fixedDelay = 10000) //Execute 10 seconds after the last execution

fixedRate: indicates how long after the last execution starts. The usage is the same as above.
initialDelay: indicates how long the first task execution is delayed. The usage is the same as above.

@Scheduled thread issues

@Scheduled is executed by a single thread by default. Multiple @ scheduled tasks use the same thread. If a task is a time-consuming operation, other scheduled tasks will wait for the execution of the task to complete, resulting in congestion.
For example, we give an example below: two unrelated scheduled tasks: printing log print and generating table generate need to be triggered once per second. If it takes 3 seconds to execute generate once, both print and generate can only be executed after the execution of generate this time, which will not achieve the effect of executing print and generate once a second.

Let's test it with code

We use thread Sleep () simulates the time taken to execute the method. Both are executed once per second, in which test1 sleeps for 3 seconds and the simulation takes time.

 @Scheduled(cron = "* * * * * ?")
    public void test1() {
        /**
         * Once per second
         */
        logger.info("test1 Execute and sleep for 3 seconds: " + System.currentTimeMillis());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        logger.info("test1 end of execution: " + System.currentTimeMillis());
    }

 @Scheduled(cron = "* * * * * ?")
    public void test2() {
        /**
         * Once per second
         */
        logger.info("test2 implement: " + System.currentTimeMillis());
    }

Operation results:

2022-02-14 14:09:41.004  INFO 2484 --- [   scheduling-1] club.yunzhi.log.task.DayLogTask          : test1 Execute and sleep for 3 seconds: 1644829781004
2022-02-14 14:09:44.007  INFO 2484 --- [   scheduling-1] club.yunzhi.log.task.DayLogTask          : test1 end of execution: 1644829784007
2022-02-14 14:09:44.010  INFO 2484 --- [   scheduling-1] club.yunzhi.log.task.DayLogTask          : test2 implement: 1644829784010
2022-02-14 14:09:45.005  INFO 2484 --- [   scheduling-1] club.yunzhi.log.task.DayLogTask          : test1 Execute and sleep for 3 seconds: 1644829785005
2022-02-14 14:09:48.007  INFO 2484 --- [   scheduling-1] club.yunzhi.log.task.DayLogTask          : test1 end of execution: 1644829788007
2022-02-14 14:09:48.008  INFO 2484 --- [   scheduling-1] club.yunzhi.log.task.DayLogTask          : test2 implement: 1644829788008
2022-02-14 14:09:49.009  INFO 2484 --- [   scheduling-1] club.yunzhi.log.task.DayLogTask          : test2 implement: 1644829789009
2022-02-14 14:09:49.011  INFO 2484 --- [   scheduling-1] club.yunzhi.log.task.DayLogTask          : test1 Execute and sleep for 3 seconds: 1644829789011
2022-02-14 14:09:52.012  INFO 2484 --- [   scheduling-1] club.yunzhi.log.task.DayLogTask          : test1 end of execution: 1644829792012
2022-02-14 14:09:52.013  INFO 2484 --- [   scheduling-1] club.yunzhi.log.task.DayLogTask          : test2 implement: 1644829792013
2022-02-14 14:09:53.005  INFO 2484 --- [   scheduling-1] club.yunzhi.log.task.DayLogTask          : test1 Execute and sleep for 3 seconds: 1644829793005

It can be seen that the threads executing are all [scheduling-1], and the execution interval of test2 is not fixed, which is set to 1s, but sometimes 1s, sometimes 3s and 4s.

Then, how to make each scheduled task execute on time according to the configured timing rules? This requires us to configure timed tasks in a multi-threaded manner.

Executing methods asynchronously using @ Async

The same as adding scheduled tasks, @ EnableAsync is added to the entry class to enable asynchronous execution. Add @ Async annotation on the scheduled task to identify the asynchronous execution of the method. The method code is the same as above.

    @Async  +
    @Scheduled(cron = "* * * * * ?")
    public void test1() {

At this time, we will execute the code to see the effect:

2022-02-14 14:45:04.001  INFO 2608 --- [         task-4] club.yunzhi.log.task.DayLogTask          : test1 Execute and sleep for 3 seconds: 1644831904001
2022-02-14 14:45:04.001  INFO 2608 --- [         task-1] club.yunzhi.log.task.DayLogTask          : test1 end of execution: 1644831904001
2022-02-14 14:45:05.002  INFO 2608 --- [         task-2] club.yunzhi.log.task.DayLogTask          : test1 end of execution: 1644831905002
2022-02-14 14:45:05.002  INFO 2608 --- [         task-5] club.yunzhi.log.task.DayLogTask          : test1 Execute and sleep for 3 seconds: 1644831905002
2022-02-14 14:45:05.003  INFO 2608 --- [   scheduling-1] club.yunzhi.log.task.DayLogTask          : test2 implement: 1644831905003
2022-02-14 14:45:06.001  INFO 2608 --- [   scheduling-1] club.yunzhi.log.task.DayLogTask          : test2 implement: 1644831906001
2022-02-14 14:45:06.001  INFO 2608 --- [         task-6] club.yunzhi.log.task.DayLogTask          : test1 Execute and sleep for 3 seconds: 1644831906001
2022-02-14 14:45:06.008  INFO 2608 --- [         task-3] club.yunzhi.log.task.DayLogTask          : test1 end of execution: 1644831906008
2022-02-14 14:45:07.001  INFO 2608 --- [   scheduling-1] club.yunzhi.log.task.DayLogTask          : test2 implement: 1644831907001
2022-02-14 14:45:07.001  INFO 2608 --- [         task-4] club.yunzhi.log.task.DayLogTask          : test1 end of execution: 1644831907001
2022-02-14 14:45:07.002  INFO 2608 --- [         task-7] club.yunzhi.log.task.DayLogTask          : test1 Execute and sleep for 3 seconds: 1644831907002

Although the method takes 3 seconds to execute, each task is triggered every second and executed on time. It can be seen from the previous thread number that different threads are used for task execution. test2 uses the thread number of scheduling-1, once per second. test1 asynchrony uses multiple thread numbers.

@The default number of Async thread pools is 8.
The default @ Async can cope with general scenarios, but there will be some risks if the concurrency is high. For example, excessive overhead, memory overflow, etc. To make the service run stably, we can customize and configure the thread pool, and then let the methods that need asynchronous execution specify to run with the thread pool.

For details, please refer to this article: https://www.interhorse.cn/a/3...

In the current project, the method of timed task execution is fast, and the interval is at the minute level, so multi-threaded timed task is not used.

The above is related to the scheduled task.

Topics: Spring Boot