stay Java In most applications, interactive processing is realized by means of synchronization; However, when dealing with the interaction with third-party systems, it is easy to cause slow response. Before, most of them used multithreading to complete such tasks. In fact, in spring 3. After X, @ Async has been built in to perfectly solve this problem. This article will complete the introduction to the usage of @ Async.
1. What is asynchronous call?
Before explaining asynchronous invocation, let's look at the definition of synchronous invocation; Synchronization is the sequential execution of the whole processing process. When each process is completed, the results are returned. Asynchronous calling is just sending the called instruction, and the caller does not need to wait for the called method to be fully executed; Instead, continue with the following process.
For example, in a call, you need to call three process methods a, B and C in sequence; If they are all synchronous calls, they need to be executed in sequence before the process is completed; If B is an asynchronous calling method, after calling A after executing the B, it does not wait for B to complete. Instead, the execution starts calling C. After C is executed, it means that the process is finished.
2. Conventional asynchronous call processing method
In Java, when dealing with similar scenarios, it is generally based on creating an independent thread to complete the corresponding asynchronous call logic, through the execution process between the main thread and different threads, so that after the independent thread is started, the main thread continues to execute without stagnation.
3. @Async introduction
In Spring, the method based on @ Async annotation is called asynchronous method; When these methods are executed, they will be executed in a separate thread. The caller can continue other operations without waiting for it to complete.
Note: the function modified by @ Async should not be defined as static type, so the asynchronous call will not take effect, and the following error will be reported:
From the exception information JedisConnectionException: Could not get a resource from the pool, we can easily imagine that the asynchronous task was still executing when the application was closed. Because the Redis connection pool was destroyed first, the above error was reported for the operation to access Redis in the asynchronous task. Therefore, we conclude that the above implementation method is not elegant when the application is closed, so what should we do? The settings are as follows: executor.setWaitForTasksToCompleteOnShutdown(true); executor.setAwaitTerminationSeconds(60);
How to enable @ Async in Spring
1. Enabling method based on Java configuration:
@Configuration @EnableAsync public class SpringAsyncConfig { ... }
The configuration in springboot is:
@EnableSwagger2 @EnableAsync @EnableTransactionManagement public class SettlementApplication { public static void main(String[] args) { SpringApplication.run(SettlementApplication.class, args); } @Bean public RestTemplate restTemplate() { return new RestTemplate(); } }
2. The enabling method based on XML configuration file is configured as follows:
<task:executor id="myexecutor" pool-size="5" /> <task:annotation-driven executor="myexecutor"/>
These are two ways to define.
4. No return value call based on @ Async
Examples are as follows:
@Async //Annotation use public void asyncMethodWithVoidReturnType() { System.out.println("Execute method asynchronously. " + Thread.currentThread().getName()); }
The method of use is very simple. One annotation can solve all problems.
5. Call based on @ Async return value
Examples are as follows:
@Async public Future<String> asyncMethodWithReturnType() { System.out.println("Execute method asynchronously - " + Thread.currentThread().getName()); try { Thread.sleep(5000); return new AsyncResult<String>("hello world !!!!"); } catch (InterruptedException e) { // } return null; }
The above example shows that the returned data type is Future, which is an interface. The specific result type is AsyncResult, which needs attention.
Example of calling an asynchronous method that returns a result:
public void testAsyncAnnotationForMethodsWithReturnType() throws InterruptedException, ExecutionException { System.out.println("Invoking an asynchronous method. " + Thread.currentThread().getName()); Future<String> future = asyncAnnotationExample.asyncMethodWithReturnType(); while (true) { ///Loop judgment is used here to wait for the result information if (future.isDone()) { //Judge whether the execution is completed System.out.println("Result from asynchronous process - " + future.get()); break; } System.out.println("Continue doing something else. "); Thread.sleep(1000); } }
Analysis: the result information of these asynchronous methods is obtained by constantly checking the status of Future to obtain whether the current asynchronous method has completed execution.
6. Based on the exception handling mechanism in @ Async call
In an asynchronous method, if an exception occurs, it is imperceptible to the caller caller.
Take an example:
A. Asynchronous method class:
/** * @author duanxz * 2018 August 2, 2014 4:36:49 PM */ @Component public class AsyncServiceTest { @Async public void test() { System.out.println("AsyncServiceTest.test()"); throw new IllegalArgumentException("sssssssssssssssssss"); } }
B. Business call class
//... test.test(); System.out.println("channelUploadFileList()" + LocalDateTime.now()); MLogModel model = new MLogModel(); //...
Look at the results:
If you really need to handle exceptions, follow the following methods:
1. Customize the task executor that implements asynctask executor, and define the logic and method of handling specific exceptions here.
2. Configure a custom TaskExecutor to replace the built-in task executor
Example step 1, custom TaskExecutor
public class ExceptionHandlingAsyncTaskExecutor implements AsyncTaskExecutor { private AsyncTaskExecutor executor; public ExceptionHandlingAsyncTaskExecutor(AsyncTaskExecutor executor) { this.executor = executor; } Wrapped in separate threads,@Async This is its essence public void execute(Runnable task) { executor.execute(createWrappedRunnable(task)); } public void execute(Runnable task, long startTimeout) { /Wrapped in separate threads,@Async This is its essence executor.execute(createWrappedRunnable(task), startTimeout); } public Future submit(Runnable task) { return executor.submit(createWrappedRunnable(task)); //Wrapped in separate threads, @ Async is its essence. } public Future submit(final Callable task) { //Wrapped in separate threads, @ Async is its essence. return executor.submit(createCallable(task)); } private Callable createCallable(final Callable task) { return new Callable() { public T call() throws Exception { try { return task.call(); } catch (Exception ex) { handle(ex); throw ex; } } }; } private Runnable createWrappedRunnable(final Runnable task) { return new Runnable() { public void run() { try { task.run(); } catch (Exception ex) { handle(ex); } } }; } private void handle(Exception ex) { //Where specific exception logic is handled System.err.println("Error during @Async execution: " + ex); } }
Analysis: it can be found that it implements the asynctask executor, which uses independent threads to execute each specific method operation. In createCallable and createWrapperRunnable, the exception handling method and mechanism are defined.
handle() is where we need to focus on exception handling in the future.
Content in configuration file:
<task:annotation-driven executor="exceptionHandlingTaskExecutor" scheduler="defaultTaskScheduler" /> <bean id="exceptionHandlingTaskExecutor" class="nl.jborsje.blog.examples.ExceptionHandlingAsyncTaskExecutor"> <constructor-arg ref="defaultTaskExecutor" /> </bean> <task:executor id="defaultTaskExecutor" pool-size="5" /> <task:scheduler id="defaultTaskScheduler" pool-size="1" />
Analysis: the configuration here uses a custom taskexecutor to replace the default taskexecutor.
Or (look at the source code of Aysnc first):
public interface AsyncConfigurer { Executor getAsyncExecutor(); AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler(); }
AsyncConfigurerSupport is an implementation of the AsyncConfigurer interface, but it does nothing.
public class AsyncConfigurerSupport implements AsyncConfigurer { @Override public Executor getAsyncExecutor() { return null; } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return null; } }
@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; } }
You can implement the AsyncConfigurer interface to handle exceptions
@Configuration @EnableAsync public class SpringAsyncConfigurer implements AsyncConfigurer { @Override public Executor getAsyncExecutor() { return new ThreadPoolTaskExecutor(); } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return new CustomAsyncExceptionHandler(); } }
Exception handling class:
public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler { @Override public void handleUncaughtException(Throwable throwable, Method method, Object... obj) { System.out.println("Exception message - " + throwable.getMessage()); System.out.println("Method name - " + method.getName()); for (Object param : obj) { System.out.println("Parameter value - " + param); } } }
7. Transaction processing mechanism in @ async call
The @ Async annotation method is also applicable to @ Transactional annotation; When it calls the database operation, it will not be able to generate the control of transaction management, because it is an operation based on asynchronous processing.
How do you add transaction management to these operations? You can put the method that needs transaction management operation inside the asynchronous method, and add @ Transactional. XML to the internally called method
For example, method A uses @ Async/@Transactional to label, but cannot produce the purpose of transaction control.
Method B is marked by @Async, C and D are invoked in B, and @Transactional is annotated by C/D respectively, so that the purpose of transaction control can be realized.
8. Definition of asynchronous thread pool
8.1. A thread pool
@Configuration @EnableAsync public class SpringAsyncConfig { @Bean public AsyncTaskExecutor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setMaxPoolSize(10); return executor; } }
8.2. Multiple thread pools
@Configuration @EnableAsync public class SpringAsyncConfig { @Bean(name = "threadPoolTaskExecutor1") public Executor threadPoolTaskExecutor() { return new ThreadPoolTaskExecutor(); } @Bean(name = "threadPoolTaskExecutor2") public Executor threadPoolTaskExecutor() { return new ThreadPoolTaskExecutor(); } }
call
@Async("threadPoolTaskExecutor1") public void asyncMethodWithConfiguredExecutor() { System.out.println("Execute method with configured executor - " + Thread.currentThread().getName()); }
9. Summary
Through the above description, the methods and precautions used by @ Async should be