In the last article we showed how to use the @async annotation to create asynchronous tasks. I can use this method to implement some concurrent operations to speed up the execution of tasks. However, if you simply create and use it as described above, you may still run into some problems. What’s the problem with existence? Is there a problem or risk with the implementation of asynchronous task acceleration?

@RestController
public class HelloController {

    @Autowired
    private AsyncTasks asyncTasks;
        
    @GetMapping("/hello")
    public String hello(a) {
        // Divide the parallel processing logic into three asynchronous tasks to execute simultaneously
        CompletableFuture<String> task1 = asyncTasks.doTaskOne();
        CompletableFuture<String> task2 = asyncTasks.doTaskTwo();
        CompletableFuture<String> task3 = asyncTasks.doTaskThree();
        
        CompletableFuture.allOf(task1, task2, task3).join();
        return "Hello World"; }}Copy the code

Although, in terms of a single interface call, there is no problem. However, when the interface is frequently invoked by the client, the number of asynchronous tasks increases dramatically: 3 x n (n is the number of requests), and if the task is not processed fast enough, it is likely to run out of memory. So why do you run out of memory? The root cause is that Spring Boot’s default thread pool for asynchronous tasks is configured like this:

There are two important parameters that I’ve highlighted here that need to be looked at:

  • queueCapacity: The capacity of the buffer queue, which defaults to the maximum value of INT (2 ^ 31 -1).
  • maxSize: Maximum number of threads allowed. Default is the maximum value of INT (2 ^ 31 -1).

So, by default, a typical task queue can fill up memory. So, when we actually use it, we also need to do some basic configuration on the execution thread pool of asynchronous tasks to prevent overflow of memory and make the service unavailable.

Configure the default thread pool

The default thread pool configuration is very simple and only needs to be completed in the configuration file. The main parameters are as follows:

spring.task.execution.pool.core-size=2
spring.task.execution.pool.max-size=5
spring.task.execution.pool.queue-capacity=10
spring.task.execution.pool.keep-alive=60s
spring.task.execution.pool.allow-core-thread-timeout=true
spring.task.execution.shutdown.await-termination=false
spring.task.execution.shutdown.await-termination-period=
spring.task.execution.thread-name-prefix=task-
Copy the code

The configuration meanings are as follows:

  • spring.task.execution.pool.core-size: Number of initialized threads when the thread pool is created. The default is 8
  • spring.task.execution.pool.max-size: Specifies the maximum number of threads in the thread pool. The default value is int
  • spring.task.execution.pool.queue-capacity: The queue used to buffer the execution of the task. The default value is int Max
  • spring.task.execution.pool.keep-alive: The amount of time a thread is allowed to remain idle before terminating
  • spring.task.execution.pool.allow-core-thread-timeout: Whether to allow the core thread to timeout
  • spring.task.execution.shutdown.await-termination: Indicates whether to close the application after remaining tasks are completed
  • spring.task.execution.shutdown.await-termination-period: Indicates the maximum time to wait for remaining tasks to complete
  • spring.task.execution.thread-name-prefix: the prefix of the thread name, which allows us to view the thread pool in the log for processing tasks

First, you can perform a unit test before configuring the thread pool

@Test
public void test1(a) throws Exception {
    long start = System.currentTimeMillis();

    CompletableFuture<String> task1 = asyncTasks.doTaskOne();
    CompletableFuture<String> task2 = asyncTasks.doTaskTwo();
    CompletableFuture<String> task3 = asyncTasks.doTaskThree();

    CompletableFuture.allOf(task1, task2, task3).join();

    long end = System.currentTimeMillis();

    log.info("All tasks completed, total time:" + (end - start) + "毫秒");
}
Copy the code

Since the default thread pool has a core thread count of 8, three tasks are started at the same time, and the log output looks like this:

[task - 2] com. Didispace. Chapter76. AsyncTasks: start the task 2 [task - 3] com. Didispace. Chapter76. AsyncTasks: Began to do the task 3 com [task - 1]. The didispace. Chapter76. AsyncTasks: start a task [task - 2] com. Didispace. Chapter76. AsyncTasks: To complete the task two, time-consuming: 672 milliseconds [task - 3] com. Didispace. Chapter76. AsyncTasks: To complete the task 3, time-consuming: 4677 milliseconds com [task - 1]. The didispace. Chapter76. AsyncTasks: To complete the task, a time-consuming: 5624 milliseconds [main] C.D.C. hapter76. Chapter76ApplicationTests: mission complete, total time: 5653 millisecondsCopy the code

Next, try adding the following thread pool configuration to the configuration file

spring.task.execution.pool.core-size=2
spring.task.execution.pool.max-size=5
spring.task.execution.pool.queue-capacity=10
spring.task.execution.pool.keep-alive=60s
spring.task.execution.pool.allow-core-thread-timeout=true
spring.task.execution.thread-name-prefix=task-
Copy the code

The log output sequence is as follows:

[] task - 1 com. Didispace. Chapter76. AsyncTasks: start a task [task - 2] com. Didispace. Chapter76. AsyncTasks: Began to do the task 2 com [task - 1]. The didispace. Chapter76. AsyncTasks: to complete the task a, time-consuming: 2439 milliseconds com [task - 1]. The didispace. Chapter76. AsyncTasks: Began to do the task three [task - 2] com. Didispace. Chapter76. AsyncTasks: to complete the task two, time-consuming: 5867 milliseconds com [task - 1]. The didispace. Chapter76. AsyncTasks: To complete the task 3, time-consuming: 7894 milliseconds [main] C.D.C. hapter76. Chapter76ApplicationTests: mission complete, total time: 10363 millisecondsCopy the code
  • Task 1 and task 2 immediately occupy the core thread, and task 3 enters the queue and waits
  • Task one completes, releasing a core thread, task three moves out of the queue and occupies the core thread to start processing

From: https://blog.didispace.com/spring-boot-learning-2-7-6/