In actual project development, many business scenarios need to use asynchronous to complete, such as message notification, logging, and other very commonly used can be executed asynchronously to improve efficiency, then how to use asynchronous in the Spring framework?
There are generally two types of asynchronous operations, message queue MQ, and thread pool processing ThreadPoolExecutor
In the thread pool ThreadPoolTaskExecutor
encapsulated by ThreadPoolExecutor
provided in Spring4, @Async
is directly enabled using the annotation. This annotation makes it very convenient for us to complete asynchronous operations using Spring
Custom constant class
public class ConstantFiledUtil {
public static final String AUTHORIZATION_TOKEN = "authorizationToken";
/**
* kmall thread pool name
*/
public static final String KMALL_THREAD_POOL = "KmallThreadPool";
/**
* kmall thread name prefix
*/
public static final String KMALL_THREAD_NAME_PREFIX = "kmall-thread-";
}
Configure thread pool
@Configuration(proxyBeanMethods = false)
@EnableAsync //Open annotation
public class KmallConfig {
@Bean(ConstantFiledUtil.KMALL_THREAD_POOL)
public ThreadPoolTaskExecutor FebsShiroThreadPoolTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//配置核心线程数
executor.setCorePoolSize(5);
//Configure the maximum number of threads
executor.setMaxPoolSize(20);
//Configure queue size
executor.setQueueCapacity(200);
//The idle time allowed by the thread pool maintenance thread
executor.setKeepAliveSeconds(30);
//Configure the name prefix of threads in the thread pool
executor.setThreadNamePrefix(ConstantFiledUtil.KMALL_THREAD_NAME_PREFIX);
//When the thread pool is closed, wait for all tasks to complete before continuing to destroy other beans
executor.setWaitForTasksToCompleteOnShutdown(true);
//Set the waiting time of the task in the thread pool, if it exceeds this time, it will be forced to be destroyed, to ensure that the application can be closed in the end, rather than being blocked
executor.setAwaitTerminationSeconds(60);
// rejection-policy: How to deal with new tasks when the pool has reached max size
// CALLER_RUNS: do not perform tasks in the new thread, but by the thread where the caller is located
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//Perform initialization
executor.initialize();
return executor;
}
}
Note that you need to enable asynchrony through @EnableAsync
, otherwise it will be invalid
Custom thread task
public interface ILogService extends IService<Log> {
/**
* Paging search query log information
* @param logParams
* @return
*/
IPage getSearchLogByPage(SearchLogParams logParams);
/**
* Save log
*
* @param logSubject
*/
@Async
void saveLog(LogSubject logSubject);
}
Add the @Async
annotation to the interface or method that needs to be executed asynchronously. This method is an asynchronous method. Called in the main program, it is executed in a separate thread asynchronously.
This annotation indicates that the thread pool entered by the saveLog
method is created by the KmallThreadPool
method.
We can also specify the method name separately @Async("saveLogs")
In this way, when diary recording is performed, a separate thread executes each request and responds quickly, and time-consuming operations are left to the threads in the thread pool to execute asynchronously.
@Async
in a spring boot application is very simple:@EnableAsync
@Async
to the method that needs to be called asynchronously@Async
annotation method used should be a bean object managed by the Spring container;Note that calling asynchronous methods in the same class does not take effect: the reason is that the method calls in the default class will not be intercepted by aop, that is, the caller and the callee are in the same class, and no aspect can be generated. The object is not used by the Spring container. manage. That is, the
@Async
method does not take effect.
If you want to intercept calls between methods in the same class, you need to use the instance object in the spring container instead of the default this, because calls through bean instances will be intercepted by spring aop
The method used in this example: AsyncService asyncService = context.getBean(AsyncService.class)
; Then use this reference to call the local method to achieve the purpose of being intercepted
Note: This method can only intercept protected, default, public methods, and private methods cannot be intercepted. This is a mechanism of spring aop
.
There are only two return types of asynchronous methods:
Note that if you do not customize the thread pool of the asynchronous method, SimpleAsyncTaskExecutor
is used by default. SimpleAsyncTaskExecutor
: Not a real thread pool. This class does not reuse threads. A new thread is created every time it is called. When the concurrency is large, serious performance problems will occur.
Spring asynchronous thread pool interface TaskExecutor
See the source code
@FunctionalInterface
public interface TaskExecutor extends Executor {
void execute(Runnable var1);
}
There are many implementation classes as follows:
SimpleAsyncTaskExecutor
: Not a real thread pool. This class does not reuse threads. A new thread is created every time it is called.SyncTaskExecutor
: This class does not implement asynchronous calls, but a synchronous operation. Only suitable for places where multi-threading is not requiredConcurrentTaskExecutor
: The adaptation class of Executor, it is not recommended. If ThreadPoolTaskExecutor
does not meet the requirements, consider using this classSimpleThreadPoolTaskExecutor
: is a class of Quartz's SimpleThreadPool
. Thread pool is used by quartz and non-quartz at the same time, only need to use this classThreadPoolTaskExecutor
: Most commonly used, recommended. Its essence is the packaging of java.util.concurrent.ThreadPoolExecutor
,