🌹 welcome wechat attention [Java programming], progress a little bit every day, precipitation technology to share knowledge.

I wish you are in the college entrance examination of primary school younger brothers and girls admitted to the ideal university, college entrance examination refueling! Senior advice: newspaper volunteer must not choose computer ~🙄

Today we’re going to talk about asynchronous thread pools in SpringBoot. There’s a lot to talk about in depth here, so we’re going to split this into three parts

  • use@AsyncImplementation of asynchronous invocation and custom thread pool implementation.
  • Asynchronously invoke thread pools in SpringBootInternal implementation principle.
  • How do I use thread pool technology to do this10sThe task is reduced tomsLevel.

Stay close to me, the old driver is leaving!

The asynchronous call

The concept of asynchronous invocation is not strange to students who have learned the basics of Java. Here we take a direct look at the difference between asynchronous and synchronous code and the way to achieve asynchronous invocation in SpringBoot.

Synchronization task

/ * * *@Auther: a lei who loves talking *@Company: The Tao of Java Programming@Date: 2020/7/7 however, *@Version1.0 * /
@Component
public class MyTask {
    public static Random random =new Random();

    public void doTaskOne(a) throws Exception {
        System.out.println("Start on task one.");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(5000));
        long end = System.currentTimeMillis();
        System.out.println("Complete Task 1, Time:" + (end - start) + "毫秒");
    }

    public void doTaskTwo(a) throws Exception {
        System.out.println("Start on task two.");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(5000));
        long end = System.currentTimeMillis();
        System.out.println("Complete Task 2, Time:" + (end - start) + "毫秒");
    }

    public void doTaskThree(a) throws Exception {
        System.out.println("Start on task three.");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(5000));
        long end = System.currentTimeMillis();
        System.out.println("Complete Task 3, Time:" + (end - start) + "毫秒"); }}Copy the code

Inject the MyTask object and perform three functions.

 @RestController
    class Test{
        @Autowired
        MyTask myTask;
        @GetMapping("/")
        public void contextLoads(a) throws Exception { myTask.doTaskOne(); myTask.doTaskTwo(); myTask.doTaskThree(); }}Copy the code

Visit http://127.0.0.1:8080/ to see output similar to the following:

Start to do task 1, complete Task 1, time:3387Milliseconds to start task 2, complete Task 2, time:621Finish Task 3 in milliseconds:4395msCopy the code

The asynchronous call

Next, the asynchronous invocation technique in SpringBoot enables the concurrent execution of three tasks that do not have dependencies. In Spring Boot, the easiest way to do this is to change a synchronous function to an asynchronous one via the @async annotation.

/ * * *@Auther: a lei who loves talking *@Company: The Tao of Java Programming@Date: 2020/7/7 however, *@Version1.0 * /
@Component
public class MyTask {
    public static Random random =new Random();
    @Async
    public void doTaskOne(a) throws Exception {
        System.out.println("Start on task one.");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(5000));
        long end = System.currentTimeMillis();
        System.out.println("Complete Task 1, Time:" + (end - start) + "毫秒");
    }
    @Async
    public void doTaskTwo(a) throws Exception {
        System.out.println("Start on task two.");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(5000));
        long end = System.currentTimeMillis();
        System.out.println("Complete Task 2, Time:" + (end - start) + "毫秒");
    }
    @Async
    public void doTaskThree(a) throws Exception {
        System.out.println("Start on task three.");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(5000));
        long end = System.currentTimeMillis();
        System.out.println("Complete Task 3, Time:" + (end - start) + "毫秒"); }}Copy the code

You also need to configure @enableAsync in the Spring Boot main program for the @Async annotation to take effect

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

Test the execution again and you’ll see that the response is significantly faster, but the data is out of order. The reason is that all three functions are already executed asynchronously. After the main program is executed asynchronously, the execution order of the thread cannot be guaranteed.

You can see why I use a configurable open thread pool in the V-LoggingTool, because I store logs and don’t care about the return value of thread tasks. I need the program to execute immediately, leaving the time-consuming tasks to the thread pool.

If you must get the result of a thread’s execution, how to handle this problem simply depends on the scenario. You can use the Future’s GET to block the result to ensure that the correct data is obtained. The timeout period can be set in GET for some timeout task scenarios.

An asynchronous callback

Following the above solution, we can use a Future to return the result of an asynchronous call to sense if the thread is finished and get the return value. For those of you who know Future/Callable, this should not sound strange.

Let’s do that for all three methods

/** * @auther: chat * @company: Java programming * @date: 2020/7/7 20:12 * @version 1.0 */ @component public class MyTask {public static Random Random =new Random(); @Async public Future<String>doTaskOne() throws Exception {
        System.out.println("Start on task one.");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(5000));
        long end = System.currentTimeMillis();
        System.out.println("Complete Task 1, Time:" + (end - start) + "毫秒");
        return new AsyncResult<>("Mission one complete.");
    }
    @Async
    public Future<String> doTaskTwo() throws Exception {
        System.out.println("Start on task two.");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(5000));
        long end = System.currentTimeMillis();
        System.out.println("Complete Task 2, Time:" + (end - start) + "毫秒");
        return new AsyncResult<>("Mission two completed.");
    }
    @Async
    public Future<String> doTaskThree() throws Exception {
        System.out.println("Start on task three.");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(5000));
        long end = System.currentTimeMillis();
        System.out.println("Complete Task 3, Time:" + (end - start) + "毫秒");
        return new AsyncResult<>("Mission three completed."); }}Copy the code

Modify the test class

 @RestController
    class Test{
        @Autowired
        MyTask myTask;
        @GetMapping("/") public void contextLoads() throws Exception { /*myTask.doTaskOne(); myTask.doTaskTwo(); myTask.doTaskThree(); */ long start = System.currentTimeMillis(); Future<String> task1 = myTask.doTaskOne(); Future<String> task2 = myTask.doTaskTwo(); Future<String> task3 = myTask.doTaskThree(); task1.get(); task2.get(); task3.get(); long end = System.currentTimeMillis(); System.out.println("All tasks completed, total time:" + (end - start) + "毫秒"); }}Copy the code

To perform a

Start Task 3 Start Task 1 Start Task 2 Complete Task 3 in 1125 milliseconds complete Task 2 in 1520 milliseconds Complete Task 1 in 4344 milliseconds Complete all tasks in 4354 millisecondsCopy the code

Of course, I’m just using an example of getting an asynchronous callback. In essence, this is not a good way to write it because GET is a blocking method, and task1 will block there if it never completes. Other techniques can also be used to ensure a reasonable return value, such as CountDownLatch.

Custom thread pools

Implementing a custom thread pool in SpirngBoot is easy. Without the touch of async annotation, everyone would write a thread pool and inject it into the container, exposing the task submission method… But SpringBoot saves you a lot of work.

  • The first step is to define a thread pool in the configuration class
@EnableAsync
    @Configuration
    class TaskPoolConfig {
        @Bean("taskExecutor")
        public Executor taskExecutor(a) {
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            executor.setCorePoolSize(10);
            executor.setMaxPoolSize(20);
            executor.setQueueCapacity(200);
            executor.setKeepAliveSeconds(60);
            executor.setThreadNamePrefix("taskExecutor-");
            executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
            returnexecutor; }}Copy the code
  • Core thread count 10: The number of threads initialized when the thread pool was created
  • Maximum number of threads 20: The maximum number of threads in the thread pool. Threads exceeding the core number will be applied only after the buffer queue is full
  • Buffer queue 200: The queue used to buffer the execution of tasks
  • Allow 60 seconds of idle time for threads: Threads that exceed the core thread are destroyed when the idle time expires
  • Prefix of thread pool name: This allows us to locate the thread pool where the processing task is located
  • Thread pool handling policy for rejected tasks: CallerRun Policy is used here

Another way to write this is to implement an empty interface AsyncConfigurer that provides an initialization thread pool and an asynchronous exception handler

public interface AsyncConfigurer {
	/**
	 * The {@link Executor} instance to be used when processing async
	 * method invocations.
	 */
	@Nullable
	default Executor getAsyncExecutor(a) {
		return null;
	}
	/**
	 * The {@linkAsyncUncaughtExceptionHandler} instance to be used * when an exception is thrown during an asynchronous method execution  * with {@code void} return type.
	 */
	@Nullable
	default AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler(a) {
		return null; }}Copy the code

There are some differences between these two commonly used writing methods. Limited to space, we will go into details when we look at the source code of @async to implement asynchronous calls in the next article.

  • To use threads from the thread pool, specify the thread pool name in the @async annotation, for example:
    @Async("taskExecutor")
    public void doTaskOne(a) throws Exception {
        log.info("Start on task one.");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(5000));
        long end = System.currentTimeMillis();
        log.info("Complete Task 1, Time:" + (end - start) + "毫秒");
    }
Copy the code

Debug shows that the thread pool is used to execute.

Closing the thread pool

There are also problems with thread pooling, so I’ll briefly describe how to gracefully turn off thread pooling for one scenario.

For example, if the thread pool task is still executing and other asynchronous pools such as Redis or Mysql connection pool have stopped, the thread pool access error will be reported.

How to solve

Add the following two sentences when initializing the thread

executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(60);
Copy the code
  • SetWaitForTasksToCompleteOnShutdown (true) : used to set the thread pool closed when waiting for all the tasks are completed to destroy other beans, so the destruction of the asynchronous task will precede Redis destruction of the thread pool.
  • SetAwaitTerminationSeconds (60) : this method is used to set the thread pool task waiting time, if more than this time haven’t destroy force to destroy, to ensure that the application of the last to be closed, and not be blocked.

Ok, so much for today, in fact, it is still very simple to use, I hope you continue to pay attention to, in the next few days I will @async to realize the principle of asynchronous call, and how I use thread pool technology to shorten the response time in the development. 🤞

More wonderful good article in: Java programming way 🎁 welcome friends to pay attention to! 🌹