Spring boot annotation @ async thread pool instance details
This article mainly introduces the spring boot annotation @ async thread pool example in detail. The example code is introduced in great detail, which has a certain reference value for everyone's study or work. Friends in need can refer to it
Starting with spring 3, the @ async annotation is provided, which can be annotated on the method to call the method asynchronously. The caller will return immediately upon the call, and the actual execution of the method will be submitted to the task of the spring taskexecutor and executed by the thread in the specified thread pool.
1. TaskExecutor
The interface class of spring asynchronous thread pool is essentially Java util. concurrent. Executor
Exception thread pool implemented by spring:
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 only a synchronous operation. Only applicable where multithreading is not required
3. Concurrenttaskexecutor: the adapter class of executor, which is not recommended. If the threadpooltaskexecutor does not meet the requirements, consider using this option
Class 4 Simplethreadpooltaskexecutor: is a class of quartz's SimpleThreadPool. This class is required only if the thread pool is used by both quartz and non quartz
5. Threadpooltaskexecutor: most commonly used, recommended. Its essence is to Java util. concurrent. Packaging of ThreadPoolExecutor
2. @EnableAsync @Async
(1) For the startup class of springboot, the @ enableasync annotation enables asynchronous calls
(2) Spring defines asynchronous tasks for @ async
There are three asynchronous methods
1. The simplest asynchronous call, with the return value of void, is based on the @ async no return value call. It is directly annotated on the using class and method (it is recommended to use the method). If you need to throw an exception, you need to manually throw a new exception.
2. The asynchronous method with parameters can pass in parameters
3. The exception call returns future and will not be handled by asyncuncaughtexceptionhandler. We need to catch and handle the exception in the method or call futrue on the caller Catch exceptions for processing when getting
3. @ async application default thread pool
Spring applies the default thread pool, which means that the name of the thread pool is not specified when the @ async annotation is used. Check the source code. The default thread pool of @ async is simpleasynctaskexecutor.
Disadvantages of default thread pool
In the application of thread pool, refer to Alibaba java development specification: thread pool is not allowed to be created by executors, and the default thread pool of the system is not allowed. It is recommended to use ThreadPoolExecutor. This processing method allows development engineers to more clearly understand the operation rules of thread pool and avoid the risk of resource depletion. Disadvantages of executors' methods:
Newfixedthreadpool and newsinglethreadexecution: the main problem is that the stacked request processing queue may consume a lot of memory, even oom.
Newcachedthreadpool and newscheduledthreadpool: the problem is that the maximum number of threads is integer MAX_ Value, a very large number of threads may be created, even oom.
@The default asynchronous configuration of async uses simpleasynctaskexecutor. The thread pool creates one thread per task by default. If the system continues to create threads, the system will eventually occupy too much memory and cause outofmemoryerror error. For thread creation, simpleasynctaskexecutor provides a current limiting mechanism. The switch is controlled through the concurrencylimit attribute. When concurrencylimit > = 0, the current limiting mechanism is turned on. By default, the current limiting mechanism is turned off, that is, concurrencylimit = - 1. When it is turned off, new threads will be created to process tasks. Based on the default configuration, simpleasynctaskexecutor is not a thread pool in the strict sense and cannot achieve the function of thread reuse.
4. @ async application custom thread pool
The user-defined thread pool can control the thread pool more finely, which is convenient to adjust the size configuration of the thread pool, and the thread performs exception control and handling. When setting the system custom thread pool to replace the default thread pool, although it can be set through multiple modes, there is and can only be one thread pool generated by replacing the default thread pool (multiple classes cannot be set to inherit asyncconfigurator). The custom thread pool has the following modes:
By viewing the spring source code's default calling rules for @ async, you will first query the classes that implement the asyncconfigurer interface in the source code. The class that implements this interface is asyncconfigurersupport. However, the default configured thread pool and asynchronous processing method are empty, so you need to specify a thread pool whether you inherit or re implement the interface. And re implement the public executor getasyncexecution() method.
(1) Implementation interface asyncconfigurer
@Configuration public class AsyncConfiguration implements AsyncConfigurer { @Bean("kingAsyncExecutor") public ThreadPoolTaskExecutor executor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); int corePoolSize = 10; executor.setCorePoolSize(corePoolSize); int maxPoolSize = 50; executor.setMaxPoolSize(maxPoolSize); int queueCapacity = 10; executor.setQueueCapacity(queueCapacity); executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); String threadNamePrefix = "kingDeeAsyncExecutor-"; executor.setThreadNamePrefix(threadNamePrefix); executor.setWaitForTasksToCompleteOnShutdown(true); // 使用自定义的跨线程的请求级别线程工厂类 RequestContextThreadFactory threadFactory = RequestContextThreadFactory.getDefault(); executor.setThreadFactory(threadFactory); int awaitTerminationSeconds = 5; executor.setAwaitTerminationSeconds(awaitTerminationSeconds); executor.initialize(); return executor; } @Override public Executor getAsyncExecutor() { return executor(); } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return (ex,method,params) -> ErrorLogger.getInstance().log(String.format("执行异步任务'%s'",method),ex); } }
(2) Inherit asyncconfigurersupport
@Configuration @EnableAsync class SpringAsyncConfigurer extends AsyncConfigurerSupport { @Bean public ThreadPoolTaskExecutor asyncExecutor() { ThreadPoolTaskExecutor threadPool = new ThreadPoolTaskExecutor(); threadPool.setCorePoolSize(3); threadPool.setMaxPoolSize(3); threadPool.setWaitForTasksToCompleteOnShutdown(true); threadPool.setAwaitTerminationSeconds(60 * 15); return threadPool; } @Override public Executor getAsyncExecutor() { return asyncExecutor; } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return (ex,ex); } }
(3) Configure custom taskexecutor
Since the default thread pool of asyncconfigurator is empty in the source code, spring uses beanfactory. Com GetBean (taskexecutor. Class) first checks whether there is a wired process pool. If it is not configured, use beanfactory GetBean (default_task_executor_bean_name, executor. Class), and query whether there is a thread pool with the default name of taskexecutor. Therefore, a bean named taskexecutor can be defined in the project to generate a default thread pool. You can also declare a thread pool without specifying the name of the thread pool, which is based on taskexecutor Class.
For example:
Executor. Class: threadpoolexecutoradapter - > ThreadPoolExecutor - > abstractexecutorservice - > executorservice - > executor (in this mode, the final bottom layer is executor.class. When replacing the default thread pool, you need to set the default thread pool name as taskexecutor)
Executor. Class: threadpooltaskexecutor - > scheduling taskexecutor - > asynctaskexecutor - > taskexecutor (in this mode, the bottom layer is taskexecutor.class. When replacing the default thread pool, the thread pool name may not be specified.)
package intellif.configs; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.annotation.AsyncConfigurer; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.stereotype.Component; import java.util.concurrent.Executor; import java.util.concurrent.ThreadPoolExecutor; /** * @author liuyu * @className TaskConfiguration * @date 2019/12/17 11:40 * @description */ @Component public class TaskConfiguration implements AsyncConfigurer { private static Logger logger = LogManager.getLogger(TaskConfiguration.class); @Value("${thread.pool.corePoolSize:10}") private int corePoolSize; @Value("${thread.pool.maxPoolSize:20}") private int maxPoolSize; @Value("${thread.pool.keepAliveSeconds:4}") private int keepAliveSeconds; @Value("${thread.pool.queueCapacity:512}") private int queueCapacity; @Value("${thread.pool.waitForTasksToCompleteOnShutdown:true}") private boolean waitForTasksToCompleteOnShutdown; @Value("${thread.pool.awaitTerminationSeconds:60}") private int awaitTerminationSeconds; @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); //核心线程数 executor.setCorePoolSize(corePoolSize); //线程池最大的线程数,只有在缓冲队列满了之后,才会申请超过核心线程数的线程 executor.setMaxPoolSize(maxPoolSize); //允许线程的空闲时间,当超过了核心线程之外的线程,在空闲时间到达之后会被销毁 executor.setKeepAliveSeconds(keepAliveSeconds); ////用来缓冲执行任务的队列 executor.setQueueCapacity(queueCapacity); //线程池名的前缀,可以用于定位处理任务所在的线程池 executor.setThreadNamePrefix("taskExecutor-"); //线程池对拒绝任务的处理策略 executor.setRejectedExecutionHandler((Runnable r,ThreadPoolExecutor exe) -> { logger.warn("当前任务线程池队列已满."); }); //该方法用来设置线程池关闭的时候等待所有任务都完成后,再继续销毁其他的Bean,这样这些异步任务的销毁就会先于数据库连接池对象的销毁。 executor.setWaitForTasksToCompleteOnShutdown(waitForTasksToCompleteOnShutdown); //该方法用来设置线程池中,任务的等待时间,如果超过这个时间还没有销毁就强制销毁,以确保应用最后能够被关闭,而不是阻塞住。 executor.setAwaitTerminationSeconds(awaitTerminationSeconds); executor.initialize(); return executor; } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return (ex,params) -> logger.error("线程池执行任务发生未知异常.",ex); } }
Multiple thread pools
@Async annotation, which uses the system default or custom thread pool (instead of the default thread pool). Multiple thread pools can be set in the project. When calling asynchronously, indicate the name of the thread pool to be called, such as @ async ("new_task").
5. @ async annotation failure reason
There is no proxy class in the past. When this class is called, it is directly called internally without using the proxy class
1. The @ enableasync annotation was not added to the @ springbootapplication startup class.
2. The return value of asynchronous method using annotation @ async can only be void or future.
3. The proxy class of spring is not used. Because the implementation of @ transactional and @ async annotations is based on spring AOP, and the implementation of AOP is based on dynamic proxy mode. Then the reason why the annotation fails is obvious. It may be because the method is called by the object itself rather than the proxy object, because it does not pass through the spring container.
resolvent:
Here is a specific solution to the third case.
1. The annotation method must be public.
The 2. method must be called from another class, that is, calling from the outside of the class, the internal call of the class is invalid.
3. If you need to call from the inside of a class, you need to get its proxy class first. The following code
@Service public class AsyncService{ public void methodA(){ ... AsyncService asyncServiceProxy = SpringUtil.getBean(AsyncService.class); asyncServiceProxy .methodB(); ... } @Async public void methodB() { ... } }
In this class, we can define asynchronous multithreading methods for implementing this class in this class.
Two must be implemented:
For the tool class of springutils, manually obtain the bean method:
package intellif.util; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; /** * @author liuyu * @className SpringUtils * @date 2019/12/16 20:55 * @description */ @Component("springContextUtil") public class SpringUtils implements ApplicationContextAware { private static ApplicationContext applicationContext = null; public static ApplicationContext getApplicationContext() { return applicationContext; } @SuppressWarnings("unchecked") public static <T> T getBean(String beanId) { return (T) applicationContext.getBean(beanId); } public static <T> T getBean(Class<T> requiredType) { return (T) applicationContext.getBean(requiredType); } /** * spring容器启动后,会把 applicationContext 给自动注入进来,然后我们把 applicationContext * 赋值到静态变量中,方便后续拿到容器对象 * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext) */ @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { SpringUtils.applicationContext = applicationContext; } }
The above is the whole content of this article. I hope it will help you in your study, and I hope you will support us a lot.