From the previous article on configuring @Async thread pools for asynchronous tasks, you should have seen that there is a thread pool behind the execution of asynchronous tasks to manage the execution of tasks. In order to control the concurrency of asynchronous tasks without affecting the normal operation of the application, we must configure the thread pool accordingly to prevent the excessive use of resources. In addition to the default thread pool configuration, another scenario that is common is thread pool isolation in the case of multitasking.
What is thread pool isolation and why
If you don’t know what thread pool isolation is, why? . So, let’s take a look at the following scenario example:
@RestController
public class HelloController {
@Autowired
private AsyncTasks asyncTasks;
@GetMapping("/api-1")
public String taskOne(a) {
CompletableFuture<String> task1 = asyncTasks.doTaskOne("1");
CompletableFuture<String> task2 = asyncTasks.doTaskOne("2");
CompletableFuture<String> task3 = asyncTasks.doTaskOne("3");
CompletableFuture.allOf(task1, task2, task3).join();
return "";
}
@GetMapping("/api-2")
public String taskTwo(a) {
CompletableFuture<String> task1 = asyncTasks.doTaskTwo("1");
CompletableFuture<String> task2 = asyncTasks.doTaskTwo("2");
CompletableFuture<String> task3 = asyncTasks.doTaskTwo("3");
CompletableFuture.allOf(task1, task2, task3).join();
return ""; }}Copy the code
In the above code, there are two API interfaces, and the execution logic of both interfaces will split the execution process into three asynchronous tasks.
All right, think about it for a minute. Think about it. What would be the problem if this were implemented?
This code is fine if the API request concurrency is not high and if each task is processed fast enough. But if there is concurrency or some of the processes get stuck. These two interfaces that provide unrelated services may interact. For example, if the maximum number of threads in the current thread pool is 2, task1 and task2 in/API-1 are processed slowly and blocked. When a user calls apI-2, the service will block!
The reason for this is that by default, all asynchronous tasks created with @async share a common thread pool, so if some asynchronous tasks encounter performance problems, it will directly affect other asynchronous tasks.
To solve this problem, we need to do some thread pool isolation for asynchronous tasks, so that different asynchronous tasks do not affect each other.
Different asynchronous tasks are configured with different thread pools
Below, we come to actual operation!
Step 1: Initialize multiple thread pools like this:
@EnableAsync
@Configuration
public class TaskPoolConfig {
@Bean
public Executor taskExecutor1(a) {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(10);
executor.setKeepAliveSeconds(60);
executor.setThreadNamePrefix("executor-1-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
return executor;
}
@Bean
public Executor taskExecutor2(a) {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(10);
executor.setKeepAliveSeconds(60);
executor.setThreadNamePrefix("executor-2-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
returnexecutor; }}Copy the code
Note: specially with executor. SetThreadNamePrefix set the thread name prefix, so that we can convenient to observe specific executive order later.
Step 2: Create an asynchronous task and specify the name of the thread pool to use
@Slf4j
@Component
public class AsyncTasks {
public static Random random = new Random();
@Async("taskExecutor1")
public CompletableFuture<String> doTaskOne(String taskNo) throws Exception {
log.info("Start task: {}", taskNo);
long start = System.currentTimeMillis();
Thread.sleep(random.nextInt(10000));
long end = System.currentTimeMillis();
log.info("Complete task: {}, time: {} ms", taskNo, end - start);
return CompletableFuture.completedFuture("Mission accomplished.");
}
@Async("taskExecutor2")
public CompletableFuture<String> doTaskTwo(String taskNo) throws Exception {
log.info("Start task: {}", taskNo);
long start = System.currentTimeMillis();
Thread.sleep(random.nextInt(10000));
long end = System.currentTimeMillis();
log.info("Complete task: {}, time: {} ms", taskNo, end - start);
return CompletableFuture.completedFuture("Mission accomplished."); }}Copy the code
TaskExecutor1 and taskExecutor2, as defined in the @async annotation, are the thread pool names. Since we didn’t specify the names of the two thread pool beans in step 1, we will use the method names by default, which are taskExecutor1 and taskExecutor2.
Step 3: Write a unit test to verify this, like this:
@Slf4j
@SpringBootTest
public class Chapter77ApplicationTests {
@Autowired
private AsyncTasks asyncTasks;
@Test
public void test(a) throws Exception {
long start = System.currentTimeMillis();
Thread pool 1
CompletableFuture<String> task1 = asyncTasks.doTaskOne("1");
CompletableFuture<String> task2 = asyncTasks.doTaskOne("2");
CompletableFuture<String> task3 = asyncTasks.doTaskOne("3");
Thread pool 2
CompletableFuture<String> task4 = asyncTasks.doTaskTwo("4");
CompletableFuture<String> task5 = asyncTasks.doTaskTwo("5");
CompletableFuture<String> task6 = asyncTasks.doTaskTwo("6");
// Execute together
CompletableFuture.allOf(task1, task2, task3, task4, task5, task6).join();
long end = System.currentTimeMillis();
log.info("All tasks completed, total time:" + (end - start) + "毫秒"); }}Copy the code
In the unit test above, a total of six asynchronous tasks were started, the first three using thread pool 1 and the last three using thread pool 2.
If the core thread is set to 2 and the maximum number of threads is set to 2, what will happen?
- The three tasks in thread pool 1, Task1 and Task2, get the execution thread first, and then Task3 enters the buffer queue because there are no threads to allocate
- The three tasks in thread pool 2, Task4 and Task5, get the execution thread first, and then Task6 enters the buffer queue because there are no threads to allocate
- Task Task3 will be executed after either Task1 or Task2 has completed
- Task Task6 will be executed after either Task4 or Task5 has completed
Once the analysis is complete, run a unit test to see if it looks like this:
23:45:11 2021-09-15. 61670-369 the INFO [executor - 1-1]. Com didispace. Chapter77. AsyncTasks: Start task: 1 2021-09-15 23:45:11. 61670-369 the INFO/executor - 2-2 com. Didispace. Chapter77. AsyncTasks: Start task: 5 23:45:11 2021-09-15. 61670-369 the INFO] [executor - 2-1 com. Didispace. Chapter77. AsyncTasks: Start task: 4 23:45:11 2021-09-15. 61670-369 the INFO/executor - 1-2 com. Didispace. Chapter77. AsyncTasks: Start task: 2 2021-09-15 23:45:15. 61670-905 the INFO] [executor - 2-1 com. Didispace. Chapter77. AsyncTasks: To complete the task: 4, time-consuming: 4532 milliseconds 23:45:15 2021-09-15. 61670-905 the INFO] [executor - 2-1 com. Didispace. Chapter77. AsyncTasks: Start task: 6 23:45:18 2021-09-15. 61670-263 the INFO/executor - 1-2 com. Didispace. Chapter77. AsyncTasks: To complete the task: 2, time-consuming: 6890 milliseconds 23:45:18 2021-09-15. 61670-263 the INFO/executor - 1-2 com. Didispace. Chapter77. AsyncTasks: Start task: 3 23:45:18 2021-09-15. 61670-896 the INFO/executor - 2-2 com. Didispace. Chapter77. AsyncTasks: To complete the task: 5, time-consuming: 7523 milliseconds 23:45:19 2021-09-15. 61670-842 the INFO/executor - 1-2 com. Didispace. Chapter77. AsyncTasks: To complete the task: 3, time-consuming: 1579 milliseconds 23:45:20 2021-09-15. 61670-551 the INFO] [executor - 1-1 com. Didispace. Chapter77. AsyncTasks: To complete the task: 1, time-consuming: 9178 milliseconds 23:45:24 2021-09-15. 61670-117 the INFO] [executor - 2-1 com. Didispace. Chapter77. AsyncTasks: To complete the task: 6, time-consuming: 8212 milliseconds 23:45:24 2021-09-15. 61670-117 the INFO [main] C.D.C. hapter77. Chapter77ApplicationTests: The total time for completing all tasks is 12762 msCopy the code
Well, that’s all for today’s learning! If you have any difficulty in learning? You can join our super high quality Spring technology exchange group, participate in the exchange and discussion, better learning and progress! More Spring Boot tutorials can be clicked direct! , welcome to collect and forward support!
Code sample
For the complete project of this article, you can see the chapter7-7 project in the 2.x directory of the warehouse below:
- Github:github.com/dyc87112/Sp…
- Gitee:gitee.com/didispace/S…
If you found this article good, welcomeStar
Support, your attention is my motivation!
Welcome to pay attention to my public account: program ape DD, share the outside can not see the dry goods and thinking!