Several ways to implement timing tasks in SpringBoot

Timing tasks are also very important in our project development. For some scenarios, timing tasks must be used, such as sending emails regularly, timing statistics, etc. This article mainly talks about several ways to implement timing tasks in projects.

1. Annotation-based

This method is very simple, the main thing is to first enable the scheduled task function with @EnableScheduling, and then add @Scheduled() to the corresponding method and write the corresponding cron expression in the middle. Examples are as follows:

schedule.ScheduleTask:

java copy code import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

@Component
@EnableScheduling //Enable scheduled tasks
public class ScheduleTask {
    @Scheduled(cron = "0/5 * * * * ?") //Timed task annotation + cron expression
    public void testScheduleTask() {
        System.out.println("Execute scheduled tasks" + LocalDateTime.now());
    }
}

Cron expression parameter reference:

  • Seconds (0~59) For example, 0/5 means every 5 seconds
  • points (0~59)
  • hour (0~23)
  • A certain day of the day (0~31), needs to be calculated
  • month (0~11)
  • Day of the week (can fill in 1-7 or SUN/MON/TUE/WED/THU/FRI/SAT)

Suggestion: It is more convenient to directly generate Cron expressions online: www.matools.com/cron/

@Scheduled: In addition to supporting the flexible parameter expression cron, it also supports delayed operations such as fixedDelay, fixedRate, and initialDelay.

The basic timing task function is realized by starting the test, but if we modify the cron expression, we need to restart the entire application to take effect, which is not very convenient. If you want to modify the cron expression to take effect, you need to use the interface to implement timing tasks.

2. Interface-based

The way of the interface is that we put the timing task information in the database, and the program pulls the timing task information from the database, such as cron expressions, to realize real-time modification and other functions.

1. First create a table in the database to record scheduled tasks

sql copy code CREATE TABLE `scheduled` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NULL,
  `cron` varchar(255) NULL,
  PRIMARY KEY (`id`)
)
INSERT INTO `mydb`.`scheduled` (`id`, `name`, `cron`) VALUES (1, 'timed task 1', '0/6 * * * * ?')

2. Introduce the corresponding dependency packages of mabatis-plus and mysql into the project

xmlCopy code<dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>mybatis-plus-boot-starter</artifactId>
  <version>3.5.3.1</version>
</dependency>
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>8.0.32</version>
</dependency>

3. Configure the connection database in application.yml

yml copy code spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydb?characterEncoding=utf-8 & serverTimeZone=UTC
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver

4. Define the mapper for querying cron expressions

mapper.CronMapper:

java copy code import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

@Mapper
public interface CronMapper {
    @Select("select cron from scheduled where id=#{id}")
    String getCron(Long id);
}

5. Implement timing tasks

java copy code import com.jk.mapper.CronMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;

@Component
@EnableScheduling //Enable scheduled tasks
public class ScheduleTask implements SchedulingConfigurer {
    @Autowired
    private CronMapper cronMapper;

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.addTriggerTask(
                //Add task content
                () -> process(),
                //Set the execution cycle
                triggerContext -> {
                    //Query cron expression
                    String cron = cronMapper. getCron(1L);
                    if (cron. isEmpty()) {
                        System.out.println("cron is null");
                    }
                    return new CronTrigger(cron).nextExecutionTime(triggerContext);
                });
    }

    private void process() {
        System.out.println("Interface-based timing task");
    }
}

This method needs to implement the SchedulingConfigurer interface and rewrite the configureTasks method, then set the task content and execution cycle, etc., and start the test to realize the timing task based on the interface. At this time, we change the cron expression in the database and it will take effect in real time.

3. Multi-thread timing task

But there is a problem with the timed task defined by the above method, that is, if I execute complex logic in a timed task, what should I do if the execution time exceeds the interval time of the timed task? At this time, certain problems will arise in the execution of scheduled tasks. The details are as follows. I use thread sleep to simulate and process complex logic.

java copy code import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

@Component
@EnableScheduling //Enable scheduled tasks
public class ScheduleTask {
    @Scheduled(cron = "0/5 * * * * ?") //Timed task annotation + cron expression
    public void testScheduleTask1() throws InterruptedException {
        System.out.println("Execute scheduled task 1 " + LocalDateTime.now());
        Thread. sleep(10 * 1000);
    }

    @Scheduled(cron = "0/5 * * * * ?") //Timed task annotation + cron expression
    public void testScheduleTask2() {
        System.out.println("Execute scheduled task 2 " + LocalDateTime.now());
    }
}

It can be seen that the execution time of the two tasks has been affected, which does not correspond to the 5 seconds we set. At this point, you can use multi-threaded timing tasks

java copy code import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

@Component
@EnableScheduling //Enable scheduled tasks
@EnableAsync //Enable multi-threading
public class ScheduleTask {
    @Scheduled(cron = "0/5 * * * * ?") //Timed task annotation + cron expression
    @Async
    public void testScheduleTask1() throws InterruptedException {
        System.out.println("Execute scheduled task 1 " + LocalDateTime.now());
        Thread. sleep(10 * 1000);
    }

    @Scheduled(cron = "0/5 * * * * ?") //Timed task annotation + cron expression
    @Async
    public void testScheduleTask2() {
        System.out.println("Execute scheduled task 2 " + LocalDateTime.now());
    }
}

In this way, multi-threaded timing tasks are realized, and each timing task will not affect each other, and the execution time of timing tasks will not be affected if it takes too long.