Synchronous programming: In synchronous programming, tasks are executed one at a time. Only when one task is completed, the next task will be unblocked .
Asynchronous programming: In asynchronous programming, multiple tasks can be performed simultaneously. You can move to another task before the previous task is completed.
In Spring Boot
, we can use the @Async
annotation to implement asynchronous behavior.
1. Define an asynchronous service interfaceAsyncService.java
public interface AsyncService { void asyncMethod() throws InterruptedException; Future<String> futureMethod() throws InterruptedException; }
2. Implement the defined interfaceAsyncServiceImpl.java
@Service @Slf4j public class AsyncServiceImpl implements AsyncService { @Async @Override public void asyncMethod() throws InterruptedException { Thread.sleep(3000); log.info("Thread: [{}], Calling other service..", Thread.currentThread().getName()); } @Async @Override public Future<String> futureMethod() throws InterruptedException { Thread.sleep(5000); log.info("Thread: [{}], Calling other service..", Thread.currentThread().getName()); return new AsyncResult<>("task Done"); } }
AsyncServiceImpl
is a spring
managed bean
.
Your asynchronous method must be public and decorated with the @Async
annotation.
The return type is restricted to void
or Future
.
3. Define a controllerAsyncController.java
@EnableAsync @RestController @Slf4j public class AsyncController { @Autowired AsyncService asyncService; @GetMapping("/async") public String asyncCallerMethod() throws InterruptedException { long start = System.currentTimeMillis(); log.info("call async method, thread name: [{}]", Thread.currentThread().getName()); asyncService.asyncMethod(); String response = "task completes in :" + (System.currentTimeMillis() - start) + "milliseconds"; return response; } @GetMapping("/asyncFuture") public String asyncFuture() throws InterruptedException, ExecutionException { long start = System.currentTimeMillis(); log.info("call async method, thread name: [{}]", Thread.currentThread().getName()); Future<String> future = asyncService.futureMethod(); // 阻塞获取结果 String taskResult = future.get(); String response = taskResult + "task completes in :" + (System.currentTimeMillis() - start) + "milliseconds"; return response; } }
Key point, you need to add annotations to enable asynchronous@EnableAsync
, of course, this annotation can be added elsewhere.
When this interface is called externally, asyncMethod()
will be executed by another thread created by the default task executor, and the main thread does not need to wait for the completion of the asynchronous method execution.
4. Run it
Now let’s run it and see if it returns asynchronously.
You can see that the /async
interface is called, and the method is called in the final step.
Call /asyncFuture
and find that the return is more than 5 seconds. Isn’t it asynchronous? In fact, it is also asynchronous, as you can see from the logs, but what we return is Future
, and calling Futrue.get()
is blocked.
Now let’s see what happens if an error is reported in the exception method? Modify the asynchronous code as follows, and a runtime exception will be thrown:
#Execute the asynchronous interface again, as shown below, and the default thread pool and exception handling will be used.
We can also customize the asynchronous method's exception handling and asynchronous task executor. We need to configure AsyncUncaughtExceptionHandler
, as shown in the following code:
@Configuration public class AsynConfiguration extends AsyncConfigurerSupport { @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(3); executor.setMaxPoolSize(4); executor.setThreadNamePrefix("asyn-task-thread-"); executor.setWaitForTasksToCompleteOnShutdown(true); executor.initialize(); return executor; } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return new AsyncUncaughtExceptionHandler() { @Override public void handleUncaughtException(Throwable ex, Method method, Object... params) { System.out.println("Exception: " + ex.getMessage()); System.out.println("Method Name: " + method.getName()); ex.printStackTrace(); } }; } }
Run again, the results are as follows:
must be done by using the @EnableAsync
annotation Annotate the main application class or any direct or indirect asynchronous method caller class to enable async support. Mainly implemented through proxy mode, the default mode is Proxy
, the other is AspectJ
. Proxy mode only allows calls to be intercepted through a proxy. Never call an async method from the same class in which it is defined, it won't work.
When annotating a method with @Async
, it will create a proxy for the object based on the "proxyTargetClass
" attribute. When spring
executes this method, by default it searches for the associated thread pool definition. The only spring
framework TaskExecutor bean
or Executor bean
named "taskExecutor
" in the context. If neither of these is resolvable, the spring framework SimpleAsyncTaskExecutor
will be used by default to handle the execution of asynchronous methods.
The above is the detailed content of How SpringBoot implements asynchronous calls elegantly. For more information, please follow other related articles on the PHP Chinese website!