Teach you to implement SpringBoot parallel tasks elegantly

  • The first one: configure the parameters into the .properties file:
  • The second timing task: single-threaded and multi-threaded
    • 1. Create a scheduled task:
    • 2. Start the scheduled task:
    • 3. Execution result (single thread)
    • 4. Multi-threaded processing of timing tasks:
    • 5. Execution result (concurrency)

Timing tasks for Spring Boot:

The first one: configure the parameters into the .properties file:

code:

package com.accord.task;
 
import java.text.SimpleDateFormat;
import java.util.Date;
 
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
 
/**
 * Load task information from configuration file
 * @author Wang Jiuyin
 */
@Component
public class ScheduledTask {
 
  private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
 
  //@Scheduled(fixedDelayString = "${jobs.fixedDelay}")
  @Scheduled(fixedDelayString = "2000")
  public void getTask1() {
    System.out.println("task 1,Load task information from configuration file, current time:" + dateFormat.format(new Date()));
  }
 
  @Scheduled(cron = "${jobs.cron}")
  public void getTask2() {
    System.out.println("task 2,Load task information from configuration file, current time:" + dateFormat.format(new Date()));
  }
}
copy

application.properties file:

jobs.fixedDelay=5000
jobs.cron=0/5 * *  * * ?
copy

In SpringBootCron2Application.java:

package com.accord;
 
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
 
@SpringBootApplication
@EnableScheduling
public class SpringBootCron2Application {
 public static void main(String[] args) {
  SpringApplication.run(SpringBootCron2Application.class, args);
 }
}
copy

Note: @EnableScheduling must be added; otherwise, the task will not be started regularly!

Parameter description in @Scheduled:

  • @Scheduled(fixedRate=2000): Execute again 2 seconds after the last execution time point;
  • @Scheduled(fixedDelay=2000): Execute again 2 seconds after the last execution time point;
  • @Scheduled(initialDelay=1000, fixedDelay=2000): The execution is delayed for 1 second for the first time, and then executed again 2 seconds after the last execution time point;
  • @Scheduled(cron="* * * * * ?"): Execute according to cron rules.

Online Cron expression generator: http://cron.qqe2.com/

The background management system + user applet based on Spring Boot + MyBatis Plus + Vue & Element supports RBAC dynamic permissions, multi-tenancy, data permissions, workflow, three-party login, payment, SMS, mall and other functions. Project address: https://github.com/YunaiV/ruoyi-vue-pro

The second timing task: single-threaded and multi-threaded

1. Create a scheduled task:

package com.accord.task;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
 
/**
 * Build and execute scheduled tasks
 * @author Wang Jiuyin
 * TODO
 */
@Component
public class ScheduledTask2 {
 
    private Logger logger = LoggerFactory.getLogger(ScheduledTask2.class);
 
    private int fixedDelayCount = 1;
    private int fixedRateCount = 1;
    private int initialDelayCount = 1;
    private int cronCount = 1;
 
    @Scheduled(fixedDelay = 5000)        //fixedDelay = 5000 means that after the current method is executed 5000ms, Spring scheduling will call the method again
    public void testFixDelay() {
        logger.info("===fixedDelay: No.{}Second execution method", fixedDelayCount++);
    }
 
    @Scheduled(fixedRate = 5000)        //fixedRate = 5000 means that after the current method starts to execute 5000ms, Spring scheduling will call the method again
    public void testFixedRate() {
        logger.info("===fixedRate: No.{}Second execution method", fixedRateCount++);
    }
 
    @Scheduled(initialDelay = 1000, fixedRate = 5000)   //initialDelay = 1000 means delaying 1000ms to execute the first task
    public void testInitialDelay() {
        logger.info("===initialDelay: No.{}Second execution method", initialDelayCount++);
    }
 
    @Scheduled(cron = "0 0/1 * * * ?")  //cron accepts cron expressions, and determines timing rules according to cron expressions
    public void testCron() {
        logger.info("===initialDelay: No.{}Second execution method", cronCount++);
    }
 
}
copy

Use @Scheduled to create a scheduled task This annotation is used to mark a scheduled task method.

By looking at the @Scheduled source code, we can see that it supports a variety of parameters:

  • cron: cron expression, specifying tasks to be executed at a specific time;
  • fixedDelay: Indicates how long to execute again after the last task execution is completed, the parameter type is long, and the unit is ms;
  • fixedDelayString: Same meaning as fixedDelay, but the parameter type is changed to String;
  • fixedRate: Indicates that the task is executed at a certain frequency, the parameter type is long, and the unit is ms;
  • fixedRateString: Same meaning as fixedRate, just change the parameter type to String;
  • initialDelay: Indicates how long the delay is before executing the task for the first time, the parameter type is long, and the unit is ms;
  • initialDelayString: Same meaning as initialDelay, just change the parameter type to String;
  • zone: time zone, the default is the current time zone, generally not used.

2. Start the scheduled task:

package com.accord;
 
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
 
@SpringBootApplication
@EnableScheduling
public class SpringBootCron2Application {
 public static void main(String[] args) {
  SpringApplication.run(SpringBootCron2Application.class, args);
 }
}
copy

Note: The @EnableScheduling annotation here is used to discover the tasks annotated @Scheduled and execute them in the background. Without it, scheduled tasks cannot be executed.

To quote the original official document:

@EnableScheduling ensures that a background task executor is created. Without it, nothing gets scheduled.

3. Execution result (single thread)

A simple timed task model is completed, and the following executes springBoot to observe the execution results:

From the results entered in the console, we can see that all scheduled tasks are processed by the same thread in the same thread pool, so how do we process each scheduled task concurrently? Please continue to look down.

4. Multi-threaded processing of timing tasks:

Seeing the results of the console output, all scheduled tasks are processed through a thread. I guess a SingleThreadScheduledExecutor is set in the configuration of the scheduled tasks, so I looked at the source code and searched all the way from the ScheduledAnnotationBeanPostProcessor class. Sure enough, there is another judgment in ScheduleTasks in ScheduledTaskRegistrar (timed task registration class):

if (this.taskScheduler == null) {
 this.localExecutor = Executors.newSingleThreadScheduledExecutor();
 this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);
}
copy

This means that if the taskScheduler is empty, then a single-threaded thread pool is created for the timed task, and there is also a method for setting the taskScheduler in this class:

public void setScheduler(Object scheduler) {
 Assert.notNull(scheduler, "Scheduler object must not be null");
 if (scheduler instanceof TaskScheduler) {
  this.taskScheduler = (TaskScheduler) scheduler;
 }
 else if (scheduler instanceof ScheduledExecutorService) {
  this.taskScheduler = new ConcurrentTaskScheduler(((ScheduledExecutorService) scheduler));
 }
 else {
  throw new IllegalArgumentException("Unsupported scheduler type: " + scheduler.getClass());
 }
}
copy

In this way, the problem is very simple. We only need to explicitly set a ScheduledExecutorService by calling this method to achieve the concurrency effect. All we have to do is to implement the SchedulingConfigurer interface and rewrite the configureTasks method;

package com.accord.task;
 
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
 
import java.util.concurrent.Executors;
 
/**
 * Multi-threaded execution of timed tasks
 * @author Wang Jiuyin
 */
@Configuration
//All scheduled tasks are placed in a thread pool, and different threads are used when the scheduled tasks are started.
public class ScheduleConfig implements SchedulingConfigurer {
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        //Set a timing task thread pool with a length of 10
        taskRegistrar.setScheduler(Executors.newScheduledThreadPool(10));
    }
}
copy

5. Execution result (concurrency)

From the results output by the console, it can be seen that each scheduled task is processed by a different thread.

Tags: Linux Java Spring Spring Boot

Posted by zenabi on Mon, 12 Dec 2022 09:21:18 +0530