Introduction to Spring ThreadPoolTaskExecutor thread pool

Let’s first take a look at the following explanation in the Alibaba Java Development Manual:

[Mandatory] Thread resources must be provided through the thread pool, and explicit creation of threads in the application is not allowed.
Description: The benefit of the thread pool is to reduce the time spent on creating and destroying threads and the overhead of system resources, and solve the problem of insufficient resources. If the thread pool is not used, it may cause the system to create a large number of similar threads, leading to memory consumption or "excessive switching" problems.

[Mandatory] Thread pools are not allowed to be created using Executors, but through ThreadPoolExecutor. This processing method allows students who write to know more clearly the operating rules of the thread pool and avoid the risk of resource exhaustion.
Note: The disadvantages of the thread pool object returned by Executors are as follows: 
1) FixedThreadPool and SingleThreadPool: The allowed request queue length is Integer.MAX_VALUE, which may accumulate a large number of requests, resulting in OOM. 
2) CachedThreadPool: The allowed number of threads to create is Integer.MAX_VALUE, which may create a large number of threads, causing OOM.

This article mainly provides some brief introduction to Spring's ThreadPoolTaskExecutor. ThreadPoolExecutor is in the JDK. In web development, when using spring, ThreadPoolExecutor is generally used.

Here's a simple example:

Basic configuration of thread pool: 

@Configuration
public class ThreadPoolConfig {

    @Bean("commonThreadPoolTaskExecutor")
    public ThreadPoolTaskExecutor getCommonThreadPoolTaskExecutor() {
        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
        // 核心线程数,在没有设置allowCoreThreadTimeOut(默认false)时,核心线程即使空下来也会一直存在
        threadPoolTaskExecutor.setCorePoolSize(5);
        // 最大线程数,最多能容纳的线程数
        threadPoolTaskExecutor.setMaxPoolSize(64);
        // 除核心线程外,线程执行完任务空下来多久自动退出,但如果allowCoreThreadTimeOut=true,这个时间也适用于核心线程数
        threadPoolTaskExecutor.setKeepAliveSeconds(300);
        // 等待队列,超过核心线程数的线程会到这里
        threadPoolTaskExecutor.setQueueCapacity(32);
        // 线程池的名称,用于定位任务所在的线程池
        threadPoolTaskExecutor.setThreadNamePrefix("commonThreadPool");
        // 拒绝策略,当线程超过maxPoolSize时的处理方式,这里通过实现RejectedExecutionHandler自定义拒绝策略,方便日志或持久化
        threadPoolTaskExecutor.setRejectedExecutionHandler(new CommonRejectedExecutionHandler());
        return threadPoolTaskExecutor;
    }

    public static class CommonRejectedExecutionHandler implements RejectedExecutionHandler {

        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            // TODO
        }
    }

}

Let’s take an example of using it. Use the thread pool to print several logs in the controller. The execute and submit methods are used here:

@RestController
@RequestMapping("/test")
public class TestController {

    @Autowired
    @Qualifier("commonThreadPoolTaskExecutor")
    private ThreadPoolTaskExecutor commonThreadPoolTaskExecutor;

    @GetMapping("/threadTest")
    public String threadTest() {

        System.out.println("-- start --");

        commonThreadPoolTaskExecutor.execute(() -> {

            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException ignored) { }

            System.out.println("execute running");
        });

        commonThreadPoolTaskExecutor.submit(() -> {

            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException ignored) { }

            System.out.println("submit running");
        });

        System.out.println("-- end --");

        return "success";
    }

}

Output result:

-- start --
-- end --
execute running
submit running

It can be seen that the main thread is executed quickly, and the time-consuming operations in the thread pool are printed out later.

 Let’s talk about the difference between execute and submit methods. If there is no need to return a value, there is basically no difference in use between the two methods. However, if you need the return value after the method execution in the thread pool, you need to use submit. This is Sometimes the main thread will end only after the child threads in the thread pool have finished.

Let’s see an example:

@GetMapping("/threadTest")
public String threadTest() {

    System.out.println("-- start --");

    Future<String> future = commonThreadPoolTaskExecutor.submit(new Callable<String>() {
        @Override
        public String call() throws Exception {

            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException ignored) { }

            System.out.println("submit running");

            return "abc";
        }
    });

    try {
        System.out.println("submit返回值:" + future.get());
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    }

    System.out.println("-- end --");

    return "success";
}

-- start --
submit running
submit return value: abc
-- end --

When there is future.get(), the main thread will execute after the child thread finishes running, because Callable has a blocking mechanism.

Guess you like

Origin blog.csdn.net/wangkaichenjuan/article/details/129840758