“This is my 21st day of participating in the First Challenge 2022. For details: First Challenge 2022”,

1. Scheduled tasks

In project development, regular tasks are often needed to help us do some content, such as regular sending of SMS/station information, data summary and statistics, business monitoring, etc., so we need to use our regular tasks. It is very simple to write regular tasks in Spring Boot. The following describes how to create a scheduled task in Spring Boot

1.1 @ Scheduled – fi xedRate way

1.1.1 pom configuration

You only need to import the Spring Boot Starter JAR package, which has built-in timing methods

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>
Copy the code

1.1.2 Add annotations

Add the ** @enablesCheduling ** annotation to the Spring Boot main class to enable the scheduled task configuration

package com;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling
public class ScheduleTaskApplication {

    public static void main(String[] args) { SpringApplication.run(ScheduleTaskApplication.class, args); }}Copy the code

1.1.3 Creating a Test Class

package com.task;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.Date;

// Scheduled task
@Component
public class SchedulerTask {
    private static final SimpleDateFormat f=new SimpleDateFormat("HH:mm:ss");

    @Scheduled(fixedRate = 5000)// Execute once every 5 seconds
    public void processFixedRate(a){
        System.out.println("ProcessFixedRate starts scheduled tasks: the current time is"+f.format(newDate())); }}Copy the code

1.1.4 Parameter Description

In the introductory example above, @scheduled (fixedRate = 5000) annotations were used to define tasks to execute every 5 seconds. The use of @Scheduled can be summarized in the following ways:

  1. @Scheduled(fixedRate = 5000) : Execution is Scheduled 5 seconds after the last time it started
  2. @Scheduled(fixedDelay = 5000) : Scheduled is executed 5 seconds after the last time
  3. @Scheduled(initialDelay=1000, fixedRate=5000) : The first delay is executed after 1 second and thereafter is executed every 5 seconds according to fixedRate’s rule

1.1.5 Running tests

1.2 @ Scheduled cron

You can implement timed tasks in another way, simply by modifying the test class

1.2.1 Modifying a Test Class

package com.task;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.Date;

// Scheduled task
@Component
public class SchedulerTask {
    private static final SimpleDateFormat f=new SimpleDateFormat("HH:mm:ss");

    @Scheduled(cron = "*/5 * * * * *")
    public void processFixedRate(a){
        System.out.println("ProcessFixedRate starts scheduled tasks: the current time is"+f.format(newDate())); }}Copy the code

1.2.2 test

1.2.3 Parameter Description

There are seven cron bits, the last of which is year. In the Spring Boot timing scheme, you only need to set six bits

  1. The first digit indicates the second. The value ranges from 0 to 59.

  2. The second value ranges from 0 to 59.

  3. The third bit indicates the hour. The value ranges from 0 to 23.

  4. The fourth digit is day/day. The value ranges from 1 to 31.

  5. The fifth digit is the date and month, ranging from 1 to 12.

  6. The sixth digit is week. The value ranges from 1 to 7. Note, 1 indicates Sunday, 2 indicates Monday.

  7. The seventh digit is the year, which can be left blank. The value ranges from 1970 to 2099

There are also some special symbols in cron that have the following meanings:

  1. (*) star, can be understood to mean every second, every minute, every day, every month, every year.. (?) Question mark: The question mark can only appear in the date and week, indicating that the value of this position is uncertain. (-) The minus sign indicates a range. For example, if “10 to 12” is used in the hour field, the value ranges from 10 to 12, that is, 10, 11, and 12

  2. (,) comma: indicates a list value. For example, if “1, 2, 4” is used in the week field, it indicates Monday, Tuesday, and Thursday

  3. (/) Slash, such as x/y, x is the start value, y is the step length, such as the first (second), 0/15 is the start of 0 seconds, every 15 seconds.

Here are some common examples: 0 0 1 * *? : Implemented at 1 am every day; 0, 5, 1 * *, right? : Implemented at 1:05 am every day;

2. Asynchronous invocation

2.1 Synchronous Invocation

Synchronous invocation refers to the execution of programs in a defined order, with each line of programs waiting for the previous line to complete

2.1.1 Defining a Task class

Create three processing functions to simulate the operation of three tasks, and the operation consumption time is randomly selected (within 10 seconds).

package com.task;

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.util.Random;

// Synchronous call
@Component
public class AsyncTask {

    public static Random random = new Random();

    
    public void testTask1(a) throws Exception{
        System.out.println("Open task one.");
        long starttime = System.currentTimeMillis();
        Thread.sleep(random.nextInt(1000));
        long endtime = System.currentTimeMillis();
        System.out.println("Time taken to complete Task one."+(endtime-starttime)+"毫秒");
    }
   
    public void testTask2(a) throws Exception{
        System.out.println("Open task two.");
        long starttime = System.currentTimeMillis();
        Thread.sleep(random.nextInt(1000));
        long endtime = System.currentTimeMillis();
        System.out.println("Time taken to complete Task two."+(endtime-starttime)+"毫秒");
    }
    
    public void testTask3(a) throws Exception{
        System.out.println("Open task three.");
        long starttime = System.currentTimeMillis();
        Thread.sleep(random.nextInt(1000));
        long endtime = System.currentTimeMillis();
        System.out.println("Time taken to complete Task three."+(endtime-starttime)+"毫秒"); }}Copy the code

2.1.2 Creating a Test Class

package com;

import com.task.AsyncTask;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class ScheduleTaskApplicationTests {

    @Test
    void contextLoads(a) {}@Autowired
    private AsyncTask asyncTask;

    @Test
    public void testTask(a) throws Exception{ asyncTask.testTask1(); asyncTask.testTask2(); asyncTask.testTask3(); }}Copy the code

2.1.3 test

TestTask1, testTask2, and testTask3 have been executed in sequence.

2.2 Asynchronous Invocation

The synchronous calls though smoothly finished three tasks, but you can see the execution time is longer, if there is no dependencies between the three task itself, can execute concurrently, synchronous calls in execution efficiency is poor, can consider through asynchronous calls asynchronous call refers to the process in order to execute concurrently, Execute subsequent programs without waiting for the result of the asynchronously invoked statement.

In Spring Boot, we can simply change a synchronous function to an asynchronous one by using the @async annotation

2.2.1 Modifying the Task Class

package com.task; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; import java.util.Random; @Component public class AsyncTask {public static Random Random = new Random(); @async public void testTask1() throws Exception{system.out.println (" Start task 1 "); long starttime = System.currentTimeMillis(); Thread.sleep(random.nextInt(1000)); long endtime = System.currentTimeMillis(); System.out.println(" time to complete task 1 "+(endtime-startTime)+" milliseconds "); } @async public void testTask2() throws Exception{system.out.println (" Start task 2 "); long starttime = System.currentTimeMillis(); Thread.sleep(random.nextInt(1000)); long endtime = System.currentTimeMillis(); System.out.println(" time to complete task 2 "+(endtime-startTime)+" milliseconds "); } @async public void testTask3() throws Exception{system.out.println (" Start task 3 "); long starttime = System.currentTimeMillis(); Thread.sleep(random.nextInt(1000)); long endtime = System.currentTimeMillis(); System.out.println(" time to complete task 3 "+(endtime-startTime)+" milliseconds "); }}Copy the code

2.2.2 modify SpringbootAsyncApplication

In order for the @async annotation to work, you also need to configure @enableAsync in the Spring Boot main program

package com;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableAsync
public class ScheduleTaskApplication {

    public static void main(String[] args) { SpringApplication.run(ScheduleTaskApplication.class, args); }}Copy the code

At this point, you can run unit tests over and over again, and you may encounter different results:

  • There is no task-related output
  • There are partial task-specific outputs
  • Out-of-order task-related output

TestTask1, testTask2, and testTask3 are executed asynchronously. After the main program is called asynchronously, the main program does not know whether the execution of these three functions is complete, because there is no other content to be executed, so the program will automatically end, resulting in incomplete or no output task related content

2.3 The Result of Asynchronous Invocation is Displayed

In order for testTask1, testTask2, and testTask3 to complete normally, suppose we need to count the total time required for the three tasks to be executed simultaneously, and then we need to wait until all three functions are completed and calculate the result. How do we determine whether the three asynchronous calls have completed? We need to use the Future to return the result of the asynchronous call

2.3.1 transformation AsyncTask

package com.task;

import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Component;

import java.util.Random;
import java.util.concurrent.Future;

// Synchronous call
@Component
public class AsyncTask {

    public static Random random = new Random();

    @Async
    public Future<String> testTask1(a) throws Exception{
        System.out.println("Open task one.");
        long starttime = System.currentTimeMillis();
        Thread.sleep(random.nextInt(1000));
        long endtime = System.currentTimeMillis();
        System.out.println("Time taken to complete Task one."+(endtime-starttime)+"毫秒");
        return new AsyncResult<>("Mission one complete.");
    }
    @Async
    public Future<String> testTask2(a) throws Exception{
        System.out.println("Open task two.");
        long starttime = System.currentTimeMillis();
        Thread.sleep(random.nextInt(1000));
        long endtime = System.currentTimeMillis();
        System.out.println("Time taken to complete Task two."+(endtime-starttime)+"毫秒");
        return new AsyncResult<>("Mission two completed.");
    }
    @Async
    public Future<String> testTask3(a) throws Exception{
        System.out.println("Open task three.");
        long starttime = System.currentTimeMillis();
        Thread.sleep(random.nextInt(1000));
        long endtime = System.currentTimeMillis();
        System.out.println("Time taken to complete Task three."+(endtime-starttime)+"毫秒");
        return new AsyncResult<>("Mission three completed."); }}Copy the code

2.3.2 Modify the test class

package com;

import com.task.AsyncTask;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.concurrent.Future;

@SpringBootTest
class ScheduleTaskApplicationTests {

    @Test
    void contextLoads(a) {}@Autowired
    private AsyncTask asyncTask;

    @Test
    public void testTask(a) throws Exception{
// asyncTask.testTask1();
// asyncTask.testTask2();
// asyncTask.testTask3();
        Future<String> taskOne = asyncTask.testTask1();
        Future<String> taskTwo = asyncTask.testTask2();
        Future<String> taskThree = asyncTask.testTask3();

        while (true) {if (taskOne.isDone()&&taskTwo.isDone()&&taskThree.isDone()){
                break;
            }
            Thread.sleep(10000); }}}Copy the code

2.3.3 test

2.3.4 summary

  • Record the start time at the beginning of the test case
  • When three asynchronous functions are called, the result object of type Future is returned
  • After three asynchronous functions are called, a loop is opened to determine whether all three asynchronous functions are finished based on the Future object returned. If both end, the loop ends; If not, wait 1 second before judging.
  • After exiting the loop, calculate the total time required for the three tasks to be executed concurrently based on the end time and start time

2.4 Asynchronously Calling a custom thread pool

The default implementation of SimpleAsyncTaskExecutor is not a real thread pool. This class does not reuse threads and creates a new thread each time it is called

2.4.1 Customizing thread Pools

package com;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

@SpringBootApplication
@EnableAsync
public class SpringbootAsyncApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootAsyncApplication.class, args);
    }

    @Bean("myTaskExecutor")
    public Executor myTaskExecutor(a) {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);// Number of core threads, the number of threads initialized when the thread pool is created
        executor.setMaxPoolSize(15);// Maximum number of threads. More than core threads will be applied only after the buffer queue is full
        executor.setQueueCapacity(200);// Buffer queue, used to buffer the execution of the task queue
        executor.setKeepAliveSeconds(60);// Threads that exceed the number of core threads are destroyed when idle time arrives
        executor.setThreadNamePrefix("myTask-");// Set this up so that we can easily locate the thread pool where the processing task is located
        executor.setWaitForTasksToCompleteOnShutdown(true);// Set the thread pool to close until all tasks are completeContinue to destroy the other Bean executor. SetAwaitTerminationSeconds (60);// This method is used to set the wait time of tasks in the thread poolForce the destruction if it exists to ensure that the application ends up being shut down rather than blocked.// The thread pool processing policy for rejected tasks: the CallerRunsPolicy policy is used here. When the thread pool has no processing capacity, this policy will be directly in theExecute method running rejected tasks in the calling thread; If the executable program has been closed, will be discarded the task executor. SetRejectedExecutionHandler (new ThreadPoolExecutor.CallerRunsPolicy());
        returnexecutor; }}Copy the code

2.4.2 transformation AsyncTask

Add the custom thread pool name after @async

package com.task;

import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Component;

import java.util.Random;
import java.util.concurrent.Future;

// Synchronous call
@Component
public class AsyncTask {

    public static Random random = new Random();

    @Async("myTaskExecutor")
    public Future<String> testTask1(a) throws Exception{
        System.out.println("Open task one.");
        long starttime = System.currentTimeMillis();
        Thread.sleep(random.nextInt(1000));
        long endtime = System.currentTimeMillis();
        System.out.println("Time taken to complete Task one."+(endtime-starttime)+"毫秒");
        return new AsyncResult<>("Mission one complete.");
    }
    @Async("myTaskExecutor")
    public Future<String> testTask2(a) throws Exception{
        System.out.println("Open task two.");
        long starttime = System.currentTimeMillis();
        Thread.sleep(random.nextInt(1000));
        long endtime = System.currentTimeMillis();
        System.out.println("Time taken to complete Task two."+(endtime-starttime)+"毫秒");
        return new AsyncResult<>("Mission two completed.");
    }
    @Async("myTaskExecutor")
    public Future<String> testTask3(a) throws Exception{
        System.out.println("Open task three.");
        long starttime = System.currentTimeMillis();
        Thread.sleep(random.nextInt(1000));
        long endtime = System.currentTimeMillis();
        System.out.println("Time taken to complete Task three."+(endtime-starttime)+"毫秒");
        return new AsyncResult<>("Mission three completed."); }}Copy the code