Springboot thread pool ThreadPoolTaskExecutor and @ Async

Springboot integrates ThreadPoolTaskExecutor thread pool

  • ThreadPoolExecutor: This is a thread pool execution class implemented by java. Basically, thread pools are created through this class
  • ThreadPoolTaskExecutor: This is a thread pool execution class implemented by springboot based on ThreadPoolExecutor

In springboot, according to Official documents According to the official document, if the thread pool is not configured, springboot will automatically configure a ThreadPoolTaskExecutor thread pool into the bean. We just need to call it in its way.

Use the default thread pool of springboot

demo test, no thread pool is configured, and the default thread pool of springboot is adopted

Test method I:

First, add @ EnableAsync in the application startup class

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

The second step is to add @ Async annotation to the methods that need to be executed asynchronously

@Service
public class AsyncTest {
    protected final Logger logger = LoggerFactory.getLogger(this.getClass());
    @Async
    public void hello(String name){
    	//The logger is used here to check the thread of execution
        logger.info("Asynchronous thread start started."+name);  
    }
}

The third step is to test and verify the test class

  @Autowired
    AsyncTest asyncTest;
    @Test
    void contextLoads() throws InterruptedException {
        asyncTest.hello("afsasfasf");
        //Be sure to sleep, otherwise the main thread is closed and the child thread has not been started
        Thread.sleep(1000);
    }

Log results:

INFO 2276 — [ main] c.h.s.t.t.ThreadpoolApplicationTests : Started ThreadpoolApplicationTests in 3.003 seconds (JVM running for 5.342) INFO 2276 — [ task-1] c.h.s.threadpool.threadpool.AsyncTest: asynchronous thread started afsasfasf

See that a new task-1 thread is opened to execute tasks. Validation succeeded.

Test method 2:

Directly call ThreadPoolTaskExecutor, modify the next test class, and directly inject ThreadPoolTaskExecutor

@Autowired
    AsyncTest asyncTest;
    @Autowired
    ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Test
    void contextLoads() throws InterruptedException {
        asyncTest.hello("async Annotation creation");
        threadPoolTaskExecutor.submit(new Thread(()->{
            logger.info("threadPoolTaskExecutor Create thread");
        }));
        //Be sure to sleep, otherwise the main thread is closed and the child thread has not been started
        Thread.sleep(1000);
    }

Print log:

INFO 12360 - [TASK-2] c.h.s.t.t.threadpoolapplicationtests: threadpooltaskexecutor creates thread INFO 12360 - [TASK-1] c.h.s.threadpool threadpool. Asynctest: asynchronous thread started Async annotation creation

Note 1: if you only use ThreadPoolTaskExecutor, you don't need to add @ EnableAsync annotation on the Application startup class.

Note 2: several tests found that ThreadPoolTaskExecutor executes faster than @ Async.

Thread pool default configuration information

It can be found in application Make relevant settings in the properties file.

# Number of core threads
spring.task.execution.pool.core-size=8  
# Maximum number of threads
spring.task.execution.pool.max-size=16
# Idle thread lifetime
spring.task.execution.pool.keep-alive=60s
# Allow core thread timeout
spring.task.execution.pool.allow-core-thread-timeout=true
# Number of thread queues
spring.task.execution.pool.queue-capacity=100
# Thread shutdown wait
spring.task.execution.shutdown.await-termination=false
spring.task.execution.shutdown.await-termination-period=
# Thread name prefix
spring.task.execution.thread-name-prefix=task-

Using a custom thread pool

First, create a ThreadPoolConfig, configure a thread pool, and set the rejection policy to CallerRunsPolicy

@Configuration
public class ThreadPoolConfig {

    @Bean("taskExecutor")
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        //Setting thread pool parameter information
        taskExecutor.setCorePoolSize(10);
        taskExecutor.setMaxPoolSize(50);
        taskExecutor.setQueueCapacity(200);
        taskExecutor.setKeepAliveSeconds(60);
        taskExecutor.setThreadNamePrefix("myExecutor--");
        taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
        taskExecutor.setAwaitTerminationSeconds(60);
        //Modify the reject policy to execute with the current thread
        taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //Initialize thread pool
        taskExecutor.initialize();
        return taskExecutor;
    }
}

Then execute the test code written before and find that the thread pool used has become a custom thread pool

INFO 12740 - [myExecutor – 2] c.h.s.t.t.threadpoolapplicationtests: threadpooltaskexecutor creates thread INFO 12740 - [myExecutor – 1] c.h.s.threadpool threadpool. Asynctest: asynchronous thread started Async annotation creation

If multiple thread pools are configured, how to specify thread pools?

@Configuration
public class ThreadPoolConfig {

       @Bean("taskExecutor")
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        //Setting thread pool parameter information
        taskExecutor.setCorePoolSize(10);
        taskExecutor.setMaxPoolSize(50);
        taskExecutor.setQueueCapacity(200);
        taskExecutor.setKeepAliveSeconds(60);
        taskExecutor.setThreadNamePrefix("myExecutor--");
        taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
        taskExecutor.setAwaitTerminationSeconds(60);
        //Modify the reject policy to execute with the current thread
        taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //Initialize thread pool
        taskExecutor.initialize();
        return taskExecutor;
    }

    @Bean("poolExecutor")
    public Executor poolExecutor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        //Setting thread pool parameter information
        taskExecutor.setCorePoolSize(10);
        taskExecutor.setMaxPoolSize(50);
        taskExecutor.setQueueCapacity(200);
        taskExecutor.setKeepAliveSeconds(60);
        taskExecutor.setThreadNamePrefix("myExecutor2--");
        taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
        taskExecutor.setAwaitTerminationSeconds(60);
        //Modify the reject policy to execute with the current thread
        taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //Initialize thread pool
        taskExecutor.initialize();
        return taskExecutor;
    }
}

Execution result:

No qualifying bean of type 'org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor' available: expected single matching bean but found 2: taskExecutor,taskPoolExecutor

Execute the test class, directly report an error, say that multiple classes are found, and do not know which class to load

Since the test class is automatically injected in this way:

@Autowired
ThreadPoolTaskExecutor threadPoolTaskExecutor; 

Considering the problem of how to match multiple classes when @ Autowired and @ Resources are injected, as long as the bean to be injected is specified during use, the corresponding thread pool will be called.

 @Autowired
    AsyncTest asyncTest;
    @Autowired
    ThreadPoolTaskExecutor poolExecutor; //Will match the thread pool @ Bean("poolExecutor")
    @Test
    void contextLoads() throws InterruptedException {
        asyncTest.hello("async Annotation creation");
        //Be sure to sleep, otherwise the main thread is closed and the child thread has not been started
        poolExecutor.submit(new Thread(()->{
            logger.info("threadPoolTaskExecutor Create thread");
        }));
        Thread.sleep(1000);
    }

Print log:

INFO 13636 - [myExecutor2 – 1] c.h.s.t.t.threadpoolapplicationtests: threadpooltaskexecutor creates thread INFO 13636 - [myExecutor – 1] c.h.s.threadpool threadpool. Asynctest: asynchronous thread started Async annotation creation

Note 1: if @ Async annotation is used, you only need to specify the bean name in the annotation to switch the corresponding thread pool, as shown below:

	@Async("taskPoolExecutor")
    public void hello(String name){
        logger.info("Asynchronous thread start started."+name);
    }

Note 2: if there are multiple thread pools, but they are not specified in the @ Async annotation, the first configured thread pool will be loaded by default.

Keywords: Spring Boot thread pool async

Added by kutte on Fri, 11 Feb 2022 11:34:58 +0200