preface
Recent projects have demand to asynchronous processing, need to use the thread pool, a long time didn’t use to the thread pool, which is basically doing JAVAweb development rarely use asynchronous processing, and is found that some old projects, threads, and the use of thread pool is confusion, there are several thread pool, some thread pool is through the administration of the spring Some are self-created, and some are directly created threads. So here’s how gracefully you use thread pools in your projects! Avoid thread pools all over your project!!
SpringBoot integrates the ThreadPoolTaskExecutor thread pool
ThreadPoolExecutor: this is a JAVA implementation of thread pool execution class, which is used to create thread pools. ThreadPoolTaskExecutor: This is a thread pool execution class that SpringBoot implements based on ThreadPoolExecutor.
In the absence of an Executor bean in the context, Spring Boot auto-configures a ThreadPoolTaskExecutor with sensible defaults that can be automatically associated to asynchronous task execution (@EnableAsync) and Spring MVC asynchronous request processing.
In SpringBoot, if a thread pool is not configured, springBoot will automatically configure a ThreadPoolTaskExecutor thread pool into the bean.
Use the default Thread pool for SpringBoot
Since SpringBoot has a default thread pool, it’s easy to call
Method 1: Call via @async annotation
Step 1: Add @enableAsync to the Application startup class
@SpringBootApplication
@EnableAsync
public class ThreadpoolApplication {
public static void main(String[] args) { SpringApplication.run(ThreadpoolApplication.class, args); }}Copy the code
Step 2: Annotate @async with 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){
// What thread is executing on
logger.info("Asynchronous thread started."+name); }}Copy the code
Step 3: Test the class for test validation
@Autowired
AsyncTest asyncTest;
@Test
void contextLoads(a) throws InterruptedException {
asyncTest.hello("afsasfasf");
// The main thread is closed and the child thread is not started
Thread.sleep(1000);
}
Copy the code
View the printed logs:
INFO 2276 — [ main] c.h.s.t.t.ThreadpoolApplicationTests : Started ThreadpoolApplicationTests in 3.003 seconds (JVM running 5.342) for the INFO – 2276 [task – 1) C.H.S.T hreadpool. Threadpool. AsyncTest: asynchronous thread start started. Afsasfasf
You can clearly see that a new task-1 thread is started to execute the task. Verification successful!!
Method 2: Call ThreadPoolTaskExecutor directly
Modify the above test class to inject ThreadPoolTaskExecutor directly
@SpringBootTest
class ThreadPoolApplicationTests {
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
AsyncTest asyncTest;
@Autowired
ThreadPoolTaskExecutor threadPoolTaskExecutor;
@Test
void contextLoads(a) throws InterruptedException {
asyncTest.hello("Async annotation Creation");
threadPoolTaskExecutor.submit(new Thread(()->{
logger.info("ThreadPoolTaskExecutor creates a thread");
}));
// The main thread is closed and the child thread is not started
Thread.sleep(1000); }}Copy the code
Check the printed logs and find that all threads have been created successfully!! :
INFO 12360 — [ task-2] c.h.s.t.t.ThreadpoolApplicationTests : ThreadPoolTaskExecutor create a thread INFO – 12360 [task – 1] C.H.S.T hreadpool. The threadpool. AsyncTest: asynchronous thread start started. The async annotations created
Note 1: If you only use ThreadPoolTaskExecutor, you don’t need the @enableAsync annotation on the Application startup class. Note 2: Multiple tests found that ThreadPoolTaskExecutor executes faster than @async!!
Thread pool default configuration information
The following is the default springBoot thread pool configuration, which can be set in the application.properties file!!
# core number of threads spring. Task. Execution. The pool. The core - size = 8 # maximum number of threads spring.. Task execution. The pool. The Max - size = 16 # survival time of the idle thread Spring, task execution. Pool. Keep - the alive = 60 s # whether to allow the core thread timeout spring.. Task execution. Pool. Allow - core - thread - a timeout = true # Thread queue number spring. Task. Execution. The pool. The queue - capacity = 100 # thread off wait for spring.. Task execution. Shutdown. Await - termination = false Spring, task execution. Shutdown. Await - termination - period = # thread name prefix spring.. Task execution. The thread name -- prefix = task -Copy the code
Drill into springBoot’s default thread pool
Spring Boot auto-configures a ThreadPoolTaskExecutor according to the official documentation. Eventually find the thread pool springboot automatic assembly class: TaskExecutionAutoConfiguration
@Bean
@ConditionalOnMissingBean
public TaskExecutorBuilder taskExecutorBuilder(TaskExecutionProperties properties, ObjectProvider<TaskExecutorCustomizer> taskExecutorCustomizers, ObjectProvider<TaskDecorator> taskDecorator) {
Pool pool = properties.getPool();
TaskExecutorBuilder builder = new TaskExecutorBuilder();
builder = builder.queueCapacity(pool.getQueueCapacity());
builder = builder.corePoolSize(pool.getCoreSize());
builder = builder.maxPoolSize(pool.getMaxSize());
builder = builder.allowCoreThreadTimeOut(pool.isAllowCoreThreadTimeout());
builder = builder.keepAlive(pool.getKeepAlive());
Shutdown shutdown = properties.getShutdown();
builder = builder.awaitTermination(shutdown.isAwaitTermination());
builder = builder.awaitTerminationPeriod(shutdown.getAwaitTerminationPeriod());
builder = builder.threadNamePrefix(properties.getThreadNamePrefix());
Stream var10001 = taskExecutorCustomizers.orderedStream();
var10001.getClass();
builder = builder.customizers(var10001::iterator);
builder = builder.taskDecorator((TaskDecorator)taskDecorator.getIfUnique());
return builder;
}
Copy the code
The thread pool is initialized by calling ThreadPoolExecutor directly.
protected ExecutorService initializeExecutor(ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) {
BlockingQueue<Runnable> queue = this.createQueue(this.queueCapacity);
ThreadPoolExecutor executor;
if (this.taskDecorator ! =null) {
executor = new ThreadPoolExecutor(this.corePoolSize, this.maxPoolSize, (long)this.keepAliveSeconds, TimeUnit.SECONDS, queue, threadFactory, rejectedExecutionHandler) {
public void execute(Runnable command) {
Runnable decorated = ThreadPoolTaskExecutor.this.taskDecorator.decorate(command);
if(decorated ! = command) { ThreadPoolTaskExecutor.this.decoratedTaskMap.put(decorated, command);
}
super.execute(decorated); }}; }else {
executor = new ThreadPoolExecutor(this.corePoolSize, this.maxPoolSize, (long)this.keepAliveSeconds, TimeUnit.SECONDS, queue, threadFactory, rejectedExecutionHandler);
}
if (this.allowCoreThreadTimeOut) {
executor.allowCoreThreadTimeOut(true);
}
this.threadPoolExecutor = executor;
return executor;
}
Copy the code
AbortPolicy: AbortPolicy: AbortPolicy: AbortPolicy: AbortPolicy: AbortPolicy: AbortPolicy: AbortPolicy: AbortPolicy
private RejectedExecutionHandler rejectedExecutionHandler = new AbortPolicy();
Use custom thread pools
In the default configuration information, there is no method to set the reject policy of the thread pool. If you need to change the reject policy, you need to define the thread pool. And if the project needs multiple custom thread pools, how to manage it?
The custom Configuration
Step 1: Create a ThreadPoolConfig to configure only one thread pool and set the rejection policy to CallerRunsPolicy
@Configuration
public class ThreadPoolConfig {
@Bean("taskExecutor")
public Executor taskExecutor(a) {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
// Set the 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);
// Change the rejection policy to execute using the current thread
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// Initialize the thread pool
taskExecutor.initialize();
returntaskExecutor; }}Copy the code
I then execute the test code I wrote earlier and discover that the thread pool used has become a custom thread pool.
INFO 12740 — [ myExecutor–2] c.h.s.t.t.ThreadpoolApplicationTests : ThreadPoolTaskExecutor create a thread INFO – 12740 [myExecutor – 1] C.H.S.T hreadpool. The threadpool. AsyncTest: Asynchronous thread starts started. Async annotation created
Step 2: If you have multiple thread pools configured, how do you specify thread pools?
@Configuration
public class ThreadPoolConfig {
@Bean("taskExecutor")
public Executor taskExecutor(a) {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
// Set the 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);
// Change the rejection policy to execute using the current thread
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// Initialize the thread pool
taskExecutor.initialize();
return taskExecutor;
}
@Bean("poolExecutor")
public Executor poolExecutor(a) {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
// Set the 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);
// Change the rejection policy to execute using the current thread
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// Initialize the thread pool
taskExecutor.initialize();
return taskExecutor;
}
@Bean("taskPoolExecutor")
public Executor taskPoolExecutor(a) {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
// Set the thread pool parameter information
taskExecutor.setCorePoolSize(10);
taskExecutor.setMaxPoolSize(50);
taskExecutor.setQueueCapacity(200);
taskExecutor.setKeepAliveSeconds(60);
taskExecutor.setThreadNamePrefix("myExecutor3--");
taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
taskExecutor.setAwaitTerminationSeconds(60);
// Change the rejection policy to execute using the current thread
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// Initialize the thread pool
taskExecutor.initialize();
returntaskExecutor; }}Copy the code
Error: found multiple classes, do not know which class to load:
No qualifying bean of type ‘org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor’ available: expected single matching bean but found 3: taskExecutor,taskPoolExecutor
Because the test class is automatically injected like this:
@Autowired
ThreadPoolTaskExecutor threadPoolTaskExecutor;
Copy the code
Considering that @autowired and @Resource injection have multiple class matching problems, we found that as long as we specify the specific bean during injection, the corresponding thread pool will be called!!
That is, modify the test class as follows:
@Autowired
AsyncTest asyncTest;
@Autowired
ThreadPoolTaskExecutor poolExecutor; // the thread pool @bean ("poolExecutor") is matched
@Test
void contextLoads(a) throws InterruptedException {
asyncTest.hello("Async annotation Creation");
// The main thread is closed and the child thread is not started
poolExecutor.submit(new Thread(()->{
logger.info("ThreadPoolTaskExecutor creates a thread");
}));
Thread.sleep(1000);
}
Copy the code
Finally, the following information is obtained:
INFO 13636 — [ myExecutor2–1] c.h.s.t.t.ThreadpoolApplicationTests : ThreadPoolTaskExecutor create a thread INFO – 13636 [myExecutor – 1] C.H.S.T hreadpool. The threadpool. AsyncTest: Asynchronous thread starts started. Async annotation created
Note 1: If the @async annotation is used, simply specify the name of the bean in the annotation to switch to the corresponding thread pool. As follows:
@Async("taskPoolExecutor")
public void hello(String name){
logger.info("Asynchronous thread started."+name);
}
Copy the code
Note 2: If there are multiple thread pools that are not specified in the @async annotation, the first configured thread pool will be loaded by default
conclusion
The four thread pool refused to strategy: www.cnblogs.com/cblogs/p/94… JAVA common four thread pool: www.cnblogs.com/zhujiabin/p… Thread pools are used to manage threads, but they are also managed in thread pool projects. Conducive to subsequent maintenance!!