brief introduction
spring The interface class of asynchronous thread pool, whose essence is Java.util.concurrent.Executor
Spring has implemented exception thread pool:
1. SimpleAsyncTaskExecutor: it is not a real thread pool. This class does not reuse threads. Each call will create a new thread.
2. SyncTaskExecutor: this class does not implement asynchronous call, but a synchronous operation. Only for places where multithreading is not required
3. ConcurrentTaskExecutor: the adapter class of Executor. It is not recommended. If the ThreadPoolTaskExecutor does not meet the requirements, consider using this class
4. SimpleThreadPoolTaskExecutor: a class of SimpleThreadPool of quartz. The thread pool is used by both quartz and non quartz, so this class is needed
5. ThreadPoolTaskExecutor: most commonly used, recommended. Its essence is to wrap java.util.concurrent.ThreadPoolExecutor
1, Thread pool configuration
Use @ EnableAsync to use multithreading; use @ Async to define a thread task; use the ThreadPoolTaskExecutor provided by spring to use thread pool.
Customize the configuration class of thread pool, add @ EnableAsync annotation on the class, and then use @ Async("thread pool name") on the method that needs to be asynchronous to execute asynchronously.
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.util.concurrent.Executor; import java.util.concurrent.ThreadPoolExecutor; /** * @descrption Thread pool initialization * version 1.0 */ @Configuration @EnableAsync public class ExecutorConfig { private static final Logger logger = LoggerFactory.getLogger(ExecutorConfig.class); @Bean(name="asyncServiceExecutor")//The name of the bean. The default method name is lowercase public Executor asyncServiceExecutor() { logger.info("start asyncServiceExecutor"); ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(20);//Configure the number of core threads executor.setMaxPoolSize(50);//Configure maximum threads executor.setKeepAliveSeconds(60);//Allow thread idle time in seconds by default executor.setQueueCapacity(10000);//Configure queue size executor.setThreadNamePrefix("BizDataAccessTP-");//Configure the name prefix of threads in the thread pool // Reflection policy: how to handle new tasks when the pool has reached max size // Call_runs: do not execute tasks in the new thread, but the thread where the caller is executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());//The processing policy of the thread pool to the rejected task executor.setWaitForTasksToCompleteOnShutdown(true); //Perform initialization executor.initialize(); return executor; } }
@Configuration @EnableAsync public class ExecutorConfig implements AsyncConfigurer { @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(20); executor.setMaxPoolSize(50); executor.setQueueCapacity(1000); executor.initialize(); return executor; } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return null; } }
@Configuration is used to define configuration classes and replace xml configuration files. The annotated classes contain one or more methods annotated by @ bean. These methods will be scanned by AnnotationConfigApplicationContext or AnnotationConfigWebApplicationContext classes and used to build bean definitions and initialize Spring containers.
2, Thread usage
1. Single thread mode
@Slf4j public class ThreadPollTest { public static void main(String[] args) { log.info("Current thread:", Thread.currentThread().getName()); thread(); log.info("End of execution."); } public static void thread() { // The actual business logic is executed here. Here we use a simple traversal to simulate Arrays.stream(new int[]{1,2,3,4,5,6,7,8,9,10}).forEach(t -> { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } log.info("Obtain number by:" + t); }); } } 10:30:22.305 [main] INFO com.haodf.master.thread.ThreadPollTest - Current thread: 10:30:22.459 [main] INFO com.haodf.master.thread.ThreadPollTest - Obtain number by:1 10:30:22.563 [main] INFO com.haodf.master.thread.ThreadPollTest - Obtain number by:2 10:30:22.666 [main] INFO com.haodf.master.thread.ThreadPollTest - Obtain number by:3 10:30:22.771 [main] INFO com.haodf.master.thread.ThreadPollTest - Obtain number by:4 10:30:22.874 [main] INFO com.haodf.master.thread.ThreadPollTest - Obtain number by:5 10:30:22.976 [main] INFO com.haodf.master.thread.ThreadPollTest - Obtain number by:6 10:30:23.077 [main] INFO com.haodf.master.thread.ThreadPollTest - Obtain number by:7 10:30:23.182 [main] INFO com.haodf.master.thread.ThreadPollTest - Obtain number by:8 10:30:23.287 [main] INFO com.haodf.master.thread.ThreadPollTest - Obtain number by:9 10:30:23.387 [main] INFO com.haodf.master.thread.ThreadPollTest - Obtain number by:10 10:30:23.387 [main] INFO com.haodf.master.thread.ThreadPollTest - End of execution.
2. Thread pool mode
@Slf4j public class ThreadPollTest { public static void main(String[] args) { log.info("Current thread:", Thread.currentThread().getName()); /** * It can also be used in the following ways, but the way of using thread pool can facilitate thread management (improve the overall performance of the program), * It can also reduce the performance consumption caused by creating a thread every time the request is executed * new Thread(() ->{ * run Business logic in method * }) * * Define a thread pool * Core poolsize: 1 * Maximum poolsize: 1 * Keep alive time: 50000 * Timeunit: timeunit.milliseconds * New linkedblockingqueue < runnable > () */ ThreadPoolExecutor executor = new ThreadPoolExecutor(1,5,50000, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>()); // Execute the business logic method serviceTest() executor.execute(new Runnable() { @Override public void run() { thread(); } }); log.info("End of execution."); } public static void thread() { // The actual business logic is executed here. Here we use a simple traversal to simulate Arrays.stream(new int[]{1,2,3,4,5,6,7,8,9,10}).forEach(t -> { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } log.info("Obtain number by:" + t); }); } } 10:42:48.116 [main] INFO com.haodf.master.thread.ThreadPollTest - Current thread: 10:42:48.120 [main] INFO com.haodf.master.thread.ThreadPollTest - End of execution. 10:42:48.269 [pool-1-thread-1] INFO com.haodf.master.thread.ThreadPollTest - Obtain number by:1 10:42:48.369 [pool-1-thread-1] INFO com.haodf.master.thread.ThreadPollTest - Obtain number by:2 10:42:48.473 [pool-1-thread-1] INFO com.haodf.master.thread.ThreadPollTest - Obtain number by:3 10:42:48.575 [pool-1-thread-1] INFO com.haodf.master.thread.ThreadPollTest - Obtain number by:4 10:42:48.678 [pool-1-thread-1] INFO com.haodf.master.thread.ThreadPollTest - Obtain number by:5 10:42:48.779 [pool-1-thread-1] INFO com.haodf.master.thread.ThreadPollTest - Obtain number by:6 10:42:48.880 [pool-1-thread-1] INFO com.haodf.master.thread.ThreadPollTest - Obtain number by:7 10:42:48.985 [pool-1-thread-1] INFO com.haodf.master.thread.ThreadPollTest - Obtain number by:8 10:42:49.090 [pool-1-thread-1] INFO com.haodf.master.thread.ThreadPollTest - Obtain number by:9 10:42:49.192 [pool-1-thread-1] INFO com.haodf.master.thread.ThreadPollTest - Obtain number by:10
The execution result of the program shows that the main thread returns the result directly, and then the thread pool executes the business logic, reducing the request response time.
3. Annotation @ EnableAsync and @ Async implementation
We found that if we need this asynchronous request in multiple requests, the efficiency of writing such redundant thread pool configuration is very low every time. So in order to improve the development efficiency of developers, spring uses @ EnableAsync to enable asynchronous support, and @ Async to perform asynchronous execution on a certain method.
@Slf4j @Controller public class ThreadPollTest { @Autowired private ThreadService threadService; @RequestMapping(value = "/test") public void test() { log.info("Current thread:", Thread.currentThread().getName()); threadService.thread(); log.info("End of execution."); } }
@EnableAsync @Slf4j @Service public class ThreadService { @Async //This is labeled as an asynchronous task. When this method is executed, the thread will be opened separately for execution public void thread() { // The actual business logic is executed here. Here we use a simple traversal to simulate Arrays.stream(new int[]{1,2,3,4,5,6,7,8,9,10}).forEach(t -> { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } log.info("Obtain number by:" + t); }); } }
2020-02-20 11:08:45.298 INFO 1483 --- [nio-8080-exec-3] com.haodf.master.thread.ThreadPollTest : Current thread: 2020-02-20 11:08:45.299 INFO 1483 --- [nio-8080-exec-3] com.haodf.master.thread.ThreadPollTest : End of execution. 2020-02-20 11:08:46.300 INFO 1483 --- [ task-2] com.haodf.master.thread.ThreadService : Obtain number by:1 2020-02-20 11:08:47.301 INFO 1483 --- [ task-2] com.haodf.master.thread.ThreadService : Obtain number by:2 2020-02-20 11:08:48.304 INFO 1483 --- [ task-2] com.haodf.master.thread.ThreadService : Obtain number by:3 2020-02-20 11:08:49.307 INFO 1483 --- [ task-2] com.haodf.master.thread.ThreadService : Obtain number by:4 2020-02-20 11:08:50.310 INFO 1483 --- [ task-2] com.haodf.master.thread.ThreadService : Obtain number by:5 2020-02-20 11:08:51.315 INFO 1483 --- [ task-2] com.haodf.master.thread.ThreadService : Obtain number by:6 2020-02-20 11:08:52.319 INFO 1483 --- [ task-2] com.haodf.master.thread.ThreadService : Obtain number by:7 2020-02-20 11:08:53.320 INFO 1483 --- [ task-2] com.haodf.master.thread.ThreadService : Obtain number by:8 2020-02-20 11:08:54.325 INFO 1483 --- [ task-2] com.haodf.master.thread.ThreadService : Obtain number by:9 2020-02-20 11:08:55.327 INFO 1483 --- [ task-2] com.haodf.master.thread.ThreadService : Obtain number by:10
The result is consistent with the result of using thread pool, but it simplifies the logic of our code writing, and @ Async annotation helps us to realize the tedious task of creating thread pool and improves our development efficiency.
Note: @ Async decorated function should not be defined as static type, so asynchronous call will not take effect
3, Instance usage
@EnableAsync @Slf4j @Configuration public class ExecutorConfig { /** * Get thread pool of business data */ @Bean(name = "bizDataAccessTaskExecutor")//The name of the bean. The default method name is lowercase public Executor bizDataAccessTaskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(20);//Number of core threads (default) executor.setMaxPoolSize(50);//Maximum threads executor.setQueueCapacity(1_0000);//Number of buffer queues executor.setThreadNamePrefix("BizDataAccessTP-");//Thread pool name prefix executor.initialize();//Initialization //After TODO exceeds these threads, it needs to block. Select setRejectedExecutionHandler or Semaphore return executor; } }
public void dealData(String indexName, String id) { bizDataAccessTaskExecutor.execute(() -> { ..... }); }