@Async annotation
Spring 3 began to provide @ Async annotation, which can be marked on methods or classes, so as to facilitate the asynchronous invocation of methods. The caller will return immediately when calling the asynchronous method, and the actual execution of the method will be submitted to the thread execution in the specified thread pool.
@Async considerations:
- @When Async is marked on a class, it means that all methods of the class are asynchronous methods.
- @Async annotated methods must be called through dependency injection (because they must be called through proxy objects). They cannot be called directly through this object, otherwise they will not take effect.
@Async usage example
1. Enable @ Async in Spring
Create a configuration class and add @ EnableAsync annotation
@Configuration @EnableAsync public class SpringAsyncConfig{}
2. Use @ Async
Create asynchronous method execution class
@Service public class AsyncService { // Asynchronous method with no return value @Async public void noReturnMethod() { String tName = Thread.currentThread().getName(); System.out.println("current thread name : " + tName); System.out.println("noReturnMethod end"); } // Asynchronous method with return value @Async public Future<String> withReturnMethod() { String tName = Thread.currentThread().getName(); System.out.println("current thread name : " + tName); return new AsyncResult<>("aaa"); } }
Create a class that calls an asynchronous method
@RestController @RequestMapping("/api/async/test/") public class AsyncController { @Autowired AsyncService asyncService; // No return value @GetMapping("/noReturn") public String noReturn() { asyncService.noReturnMethod(); return "success"; } // There is a return value @GetMapping("/withReturn") public String withReturn() { Future<String> future = asyncService.withReturnMethod(); try { String res = future.get();// Block get return value System.out.println("res = " + res); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } return "success"; } }
Spring defined thread pool class
Spring has defined the following thread pool classes:
- SimpleAsyncTaskExecutor: it is not a real thread pool. This class does not reuse threads. By default, a new thread will be created for each call.
- SyncTaskExecutor: this class does not implement asynchronous call, but a synchronous operation. Only applicable where multithreading is not required.
- ConcurrentTaskExecutor: the adapter class of Executor, which is not recommended. Consider using this class only if the ThreadPoolTaskExecutor does not meet the requirements.
- SimpleThreadPoolTaskExecutor: is the class of SimpleThreadPool of quartz. This class is required only if the thread pool is used by both quartz and non quartz.
- ThreadPoolTaskExecutor: most commonly used, recommended. Its essence is Java util. concurrent. Packaging of ThreadPoolExecutor.
Configure custom thread pool
Default thread pool for asynchronous methods
The @ EnableAsync annotation contains the following notes:
By default, Spring will be searching for an associated thread pool definition:either a unique {@link org.springframework.core.task.TaskExecutor} bean in the context,or an {@link java.util.concurrent.Executor} bean named "taskExecutor" otherwise. Ifneither of the two is resolvable, a {@link org.springframework.core.task.SimpleAsyncTaskExecutor}will be used to process async method invocations.
Translated as:
Spring will first find the default thread pool as an asynchronous method in the following two ways:
1. Find a unique bean of type TaskExecutor
2. Or a Bean of Executor type named "taskExecutor".
If neither of the above methods is found, the SimpleAsyncTaskExecutor is used as the default thread pool for asynchronous methods
The SimpleAsyncTaskExecutor thread pool executes the asynchronous methods marked with @ Async. Since the thread pool will not reuse threads, it is recommended to use a custom thread pool in the project.
Configure asynchronous method default custom thread pool
There are several ways to configure @ Async's default thread pool:
- Re implement the interface AsyncConfigurer
- Inherit AsyncConfigurerSupport
- Customize a bean of TaskExecutor type.
- Customize a Bean of Executor type named "taskExecutor".
The following shows how to configure the default custom thread pool through mode 1 (re implement the interface AsyncConfigurer):
// The configuration class implements the getAsyncExecutor() method of the AsyncConfigurer interface @Configuration @EnableAsync public class SpringAsyncConfig implements AsyncConfigurer { @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(3);//Core pool size executor.setMaxPoolSize(6);//Maximum number of threads executor.setKeepAliveSeconds(60);//Thread idle time executor.setQueueCapacity(10);//Queue degree executor.setThreadNamePrefix("my-executor1-");//Thread prefix name executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());//Configure rejection policy executor.setAllowCoreThreadTimeOut(true);// Allow core threads to be destroyed executor.initialize(); return executor; } }
Different asynchronous methods configure different thread pools
Sometimes asynchronous methods with different functions need to configure different thread pools, which can be realized by specifying the name of the thread pool on @ Async
@Configuration public class ExecutorConfig { @Bean("customExecutor-1")// Custom thread pool 1 public Executor customExecutor1() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(3);//Core pool size executor.setMaxPoolSize(6);//Maximum number of threads executor.setKeepAliveSeconds(60);//Thread idle time executor.setQueueCapacity(10);//Queue degree executor.setThreadNamePrefix("customExecutor-1-");//Thread prefix name executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());//Configure rejection policy executor.setAllowCoreThreadTimeOut(true);// Allow core threads to be destroyed executor.initialize(); return executor; } @Bean("customExecutor-2")// Custom thread pool 2 public Executor customExecutor2() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(3);//Core pool size executor.setMaxPoolSize(6);//Maximum number of threads executor.setKeepAliveSeconds(60);//Thread idle time executor.setQueueCapacity(10);//Queue degree executor.setThreadNamePrefix("customExecutor-2-");//Thread prefix name executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());//Configure rejection policy executor.setAllowCoreThreadTimeOut(true);// Allow core threads to be destroyed executor.initialize(); return executor; } }
@Async("customExecutor-1") public void method1(){} @Async("customExecutor-2") public void method2(){}
@Async exception handling
When the method returns a value with Future, Future The get () method will throw an exception, so exception capture is no problem. However, when the method does not have a return value, the main thread cannot catch the exception at this time. Additional configuration is required to handle the exception. There are the following two ways.
1. Handling exceptions through try catch
Use try catch directly in asynchronous methods to handle thrown exceptions. This method can also be used for asynchronous methods with Future return values.
2. By implementing the AsyncUncaughtExceptionHandler interface
@Configuration @EnableAsync public class SpringAsyncConfig implements AsyncConfigurer { @Override public Executor getAsyncExecutor() { // Omit the code of custom thread pool } // Custom exception handling @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return new AsyncUncaughtExceptionHandler() { @Override public void handleUncaughtException(Throwable throwable, Method method, Object... objects) { System.out.println(method.getName() + "Exception occurred! Cause of abnormality:" + throwable.getMessage() ); } }; } }