1. Scheduled tasks
In project development, we often need timed tasks to help us do some content, such as sending SMS / station information regularly, data summary and statistics, business monitoring, etc., so we need to use our timed tasks. It is very simple to write timed tasks in Spring Boot. The following is an example of how to create timed tasks in Spring Boot
1.1 @Scheduled - "xedRate" mode
1.1.1 pom configuration
Just introduce the Spring Boot Starter jar package. The timing method has been built in the Spring Boot Starter package
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> Copy code
1.1.2 add notes
Add * * @ enableshcheduling * * annotation to the main class of Spring Boot to enable the configuration of scheduled tasks
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 code
1.1.3 create 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)//Once every 5 seconds public void processFixedRate(){ System.out.println("processFixedRate Method to start the scheduled task: the current time is"+f.format(new Date())); } } Copy code
1.1.4 parameter description
In the above introductory example, the @ Scheduled(fixedRate = 5000) annotation is used to define the tasks to be executed every 5 seconds. The use of @ Scheduled can be summarized as follows:
- @Scheduled(fixedRate = 5000): execute 5 seconds after the last execution time point
- @Scheduled(fixedDelay = 5000): execute 5 seconds after the last execution
- @Scheduled(initialDelay=1000, fixedRate=5000): execute after the first delay of 1 second, and then execute every 5 seconds according to the rule of "fixedrate"
1.1.5 operation test
1.2 @ scheduled cron mode
You can also implement scheduled tasks in another way, just modify the test class
1.2.1 modify 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(){ System.out.println("processFixedRate Method to start the scheduled task: the current time is"+f.format(new Date())); } } Copy code
1.2.2 testing
1.2.3 parameter description
cron has seven digits in total, and the last digit is year. Only six digits need to be set in the Spring Boot timing scheme
- The first digit indicates seconds, and the value is 0 ~ 59;
- The second digit indicates score, with a value of 0 ~ 59;
- The third digit indicates the hour, and the value is 0 ~ 23;
- Fourth digit, date / day, value 1 ~ 31;
- The fifth digit is the date month, with a value of 1 ~ 12;
- Sixth place, week, value 1 ~ 7, Monday, Tuesday, Note: 1 means Sunday and 2 means Monday;
- The seventh digit is the year. It can be left blank. The value is 1970 ~ 2099
There are also some special symbols in cron, which have the following meanings:
- (*) asterisk, can be understood as the meaning of every, every second, every minute, every day, every month, every year (?) Question mark: the question mark can only appear in the two positions of date and week, indicating that the value of this position is uncertain (-) minus sign, indicating a range. If "10 ~ 12" is used in the hour field, it means from 10 to 12 o'clock, i.e. 10, 11 and 12
- (,) comma indicates a list value. If "1, 2, 4" is used in the week field, it indicates Monday, Tuesday and Thursday
- (/) slash, such as x/y, where x is the start value and Y is the step length. For example, in the first bit (second), 0 / 15 starts from 0 seconds and is executed every 15 seconds.
Here are some common examples of 0 *: Execute at 1 a.m. every day; 0 5 1 * * ?: At 1:05 a.m. every day;
2. Asynchronous call
2.1 synchronous call
Synchronous call means that the program is executed in sequence according to the defined order. Each line of program must wait for the execution of the previous line of program before it can be executed
2.1.1 define a Task class
Create three processing functions to simulate three operations of executing tasks respectively, and the operation consumption time is taken randomly (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() throws Exception{ System.out.println("Open Task 1"); long starttime = System.currentTimeMillis(); Thread.sleep(random.nextInt(1000)); long endtime = System.currentTimeMillis(); System.out.println("Time spent completing task 1"+(endtime-starttime)+"millisecond"); } public void testTask2() throws Exception{ System.out.println("Open Task 2"); long starttime = System.currentTimeMillis(); Thread.sleep(random.nextInt(1000)); long endtime = System.currentTimeMillis(); System.out.println("Time spent completing task 2"+(endtime-starttime)+"millisecond"); } public void testTask3() throws Exception{ System.out.println("Open Task 3"); long starttime = System.currentTimeMillis(); Thread.sleep(random.nextInt(1000)); long endtime = System.currentTimeMillis(); System.out.println("Time spent completing task 3"+(endtime-starttime)+"millisecond"); } } Copy code
2.1.2 create 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() { } @Autowired private AsyncTask asyncTask; @Test public void testTask() throws Exception{ asyncTask.testTask1(); asyncTask.testTask2(); asyncTask.testTask3(); } } Copy code
2.1.3 testing
The execution of task 1, task 2 and task 3 is completed in sequence. In other words, the execution of testTask1, testTask2 and testTask3 is completed in sequence.
2.2 asynchronous call
Although the above synchronous calls have successfully executed the three tasks, it can be seen that the execution time is relatively long. If there is no dependency between the three tasks and they can be executed concurrently, the execution efficiency of synchronous calls is relatively poor. It can be considered to execute concurrently through asynchronous calls. Asynchronous calls refer to the sequential execution of programs, Execute the following program without waiting for the return result of the asynchronous call statement.
In Spring Boot, we can simply change the original synchronous function into asynchronous function by using @ Async annotation
2.2.1 modify Task class
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(); @Async public void testTask1() throws Exception{ System.out.println("Open Task 1"); long starttime = System.currentTimeMillis(); Thread.sleep(random.nextInt(1000)); long endtime = System.currentTimeMillis(); System.out.println("Time spent completing task 1"+(endtime-starttime)+"millisecond"); } @Async public void testTask2() throws Exception{ System.out.println("Open Task 2"); long starttime = System.currentTimeMillis(); Thread.sleep(random.nextInt(1000)); long endtime = System.currentTimeMillis(); System.out.println("Time spent completing task 2"+(endtime-starttime)+"millisecond"); } @Async public void testTask3() throws Exception{ System.out.println("Open Task 3"); long starttime = System.currentTimeMillis(); Thread.sleep(random.nextInt(1000)); long endtime = System.currentTimeMillis(); System.out.println("Time spent completing task 3"+(endtime-starttime)+"millisecond"); } } Copy code
2.2.2 modify SpringbootAsyncApplication
In order for the @ Async annotation to take effect, you also need to configure @ EnableAsync in the main program of Spring Boot
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 code
At this time, you can perform unit tests repeatedly, and you may encounter various results:
- There are no task related outputs
- There are some task related outputs
- Out of order task related output
The reason is that testTask1, testTask2 and testTask3 are executed asynchronously. After the main program is called asynchronously, the main program does not care whether the three functions have been executed. Because there is no other content to be executed, the program ends automatically, resulting in incompleteness or no output of task related content
2.3 asynchronous call result return
In order to make testTask1, testTask2 and testTask3 , complete normally, suppose we need to count the total time taken for the concurrent execution of the three tasks, which requires us to wait until the transfer of the above three functions is completed, record the time and calculate the results. How can we judge whether the above three asynchronous calls have been completed? We need to use Future to return the results of asynchronous calls
2.3.1 transform 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() throws Exception{ System.out.println("Open Task 1"); long starttime = System.currentTimeMillis(); Thread.sleep(random.nextInt(1000)); long endtime = System.currentTimeMillis(); System.out.println("Time spent completing task 1"+(endtime-starttime)+"millisecond"); return new AsyncResult<>("Once the task is completed"); } @Async public Future<String> testTask2() throws Exception{ System.out.println("Open Task 2"); long starttime = System.currentTimeMillis(); Thread.sleep(random.nextInt(1000)); long endtime = System.currentTimeMillis(); System.out.println("Time spent completing task 2"+(endtime-starttime)+"millisecond"); return new AsyncResult<>("Task 2 completed"); } @Async public Future<String> testTask3() throws Exception{ System.out.println("Open Task 3"); long starttime = System.currentTimeMillis(); Thread.sleep(random.nextInt(1000)); long endtime = System.currentTimeMillis(); System.out.println("Time spent completing task 3"+(endtime-starttime)+"millisecond"); return new AsyncResult<>("Task 3 completed"); } } Copy code
2.3.2 transformation test
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() { } @Autowired private AsyncTask asyncTask; @Test public void testTask() 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 code
2.3.3 testing
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 calling the three asynchronous functions, start a loop to judge whether the three asynchronous functions have ended according to the returned Future object. If all ends, the cycle ends; If not, wait 1 second before judging.
- After jumping out of the loop, calculate the total time spent on the concurrent execution of the three tasks according to the end time start time
2.4 asynchronously calling custom thread pool
Open the asynchronous annotation @ EnableAsync method and add @ Async. The default implementation of SimpleAsyncTaskExecutor is not a real thread pool. This class does not reuse threads, and a new thread will be created every time it is called
2.4.1 custom thread pool
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() { 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);//The maximum number of threads. Threads exceeding the number of core threads will be applied only after the buffer queue is full executor.setQueueCapacity(200);//Buffer queue, which is used to buffer the queue for executing tasks executor.setKeepAliveSeconds(60);//Threads that exceed the number of core threads will be destroyed after idle time executor.setThreadNamePrefix("myTask-");//After setting, it is convenient for us to locate the thread pool where the processing task is located executor.setWaitForTasksToCompleteOnShutdown(true);//It is used to set when the thread pool is closed and wait until all tasks are completed Continue to destroy other Bean executor.setAwaitTerminationSeconds(60);//This method is used to set the waiting time of tasks in the thread pool. If it exceeds this time, it has not been set If there is destruction, it will be forcibly destroyed to ensure that the application can be closed rather than blocked in the end. //Processing strategy of the thread pool for rejecting tasks: the CallerRunsPolicy policy is adopted here. When the thread pool has no processing capacity, this policy will be directly used in the execute Method to run the rejected task in the calling thread of the method; If the executor is closed, the task is discarded executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); return executor; } } Copy code
2.4.2 transform AsyncTask
Add a 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() throws Exception{ System.out.println("Open Task 1"); long starttime = System.currentTimeMillis(); Thread.sleep(random.nextInt(1000)); long endtime = System.currentTimeMillis(); System.out.println("Time spent completing task 1"+(endtime-starttime)+"millisecond"); return new AsyncResult<>("Once the task is completed"); } @Async("myTaskExecutor") public Future<String> testTask2() throws Exception{ System.out.println("Open Task 2"); long starttime = System.currentTimeMillis(); Thread.sleep(random.nextInt(1000)); long endtime = System.currentTimeMillis(); System.out.println("Time spent completing task 2"+(endtime-starttime)+"millisecond"); return new AsyncResult<>("Task 2 completed"); } @Async("myTaskExecutor") public Future<String> testTask3() throws Exception{ System.out.println("Open Task 3"); long starttime = System.currentTimeMillis(); Thread.sleep(random.nextInt(1000)); long endtime = System.currentTimeMillis(); System.out.println("Time spent completing task 3"+(endtime-starttime)+"millisecond"); return new AsyncResult<>("Task 3 completed"); } }