Boost SpringBoot API Speed with Asynchronous Methods
This article explains why asynchronous methods improve response speed in SpringBoot, demonstrates how to enable @Async, implement service and controller code, configure custom thread pools, handle exceptions, and retrieve results, showing measurable performance gains through non‑blocking execution.
Why need async methods?
Proper use of asynchronous methods can make business interfaces extremely fast.
Async methods are suitable for business logic where tasks can be separated without affecting each other, such as generating and sending verification codes. The sending operation can be performed asynchronously, decoupling the time‑consuming task from the core response.
Similarly, reading an article involves fetching details and updating the view count. The response can be sent after fetching the article without waiting for the view‑count update, because a minor inconsistency does not affect the user experience.
Therefore, adding appropriate asynchronous methods in services can greatly improve interface response speed and enhance user experience.
Synchronous execution (single thread)
Asynchronous execution (additional thread)
Async support in SpringBoot
SpringBoot provides annotation‑based asynchronous support, so you do not need to manage threads manually.
@EnableAsync // enable async support (on main class or config)
@Async // methods annotated with @Async are executed by SpringBoot's default thread pool (SimpleAsyncTaskExecutor)Example: using Spring's async support to query an article and increase its view count.
Service layer
@Service
public class ArticleServiceImpl {
// Query article
public String selectArticle() {
// TODO simulate article query
System.out.println("查询任务线程" + Thread.currentThread().getName());
return "文章详情";
}
// Increment read count
@Async
public void updateReadCount() {
// TODO simulate time‑consuming operation
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("更新任务线程" + Thread.currentThread().getName());
}
}Controller layer
@RestController
public class AsyncTestController {
@Autowired
private ArticleServiceImpl articleService;
/**
* Simulate fetching article and then incrementing read count
*/
@PostMapping("/article")
public String getArticle() {
// Query article
String article = articleService.selectArticle();
// Increment read count
articleService.updateReadCount();
System.out.println("文章阅读业务执行完毕");
return article;
}
}Test results show a noticeable improvement in response speed, and logs confirm that the two tasks run in different threads.
Custom thread pool for async methods
SpringBoot's default thread pool can be replaced with a custom one.
Step 1: Configure custom thread pool
@EnableAsync // enable multithreading, created at startup
@Configuration
public class AsyncConfig {
@Bean("customExecutor")
public ThreadPoolTaskExecutor asyncOperationExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// core pool size
executor.setCorePoolSize(8);
// max pool size
executor.setMaxPoolSize(20);
// queue capacity
executor.setQueueCapacity(Integer.MAX_VALUE);
// keep‑alive seconds
executor.setKeepAliveSeconds(60);
// thread name prefix and group
executor.setThreadNamePrefix("AsyncOperationThread-");
executor.setThreadGroupName("AsyncOperationGroup");
// shutdown behavior
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.initialize();
return executor;
}
}Step 2: Specify the thread pool on @Async
@Async("customExecutor")
public void updateReadCount() {
// TODO simulate time‑consuming operation
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("更新文章阅读量线程" + Thread.currentThread().getName());
}Capturing exceptions in void async methods
Implement AsyncConfigurer to provide a custom Executor and AsyncUncaughtExceptionHandler.
@EnableAsync // enable multithreading, created at startup
@Configuration
public class AsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(8);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(Integer.MAX_VALUE);
executor.setKeepAliveSeconds(60);
executor.setThreadNamePrefix("AsyncOperationThread-");
executor.setThreadGroupName("AsyncOperationGroup");
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.initialize();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new CustomAsyncExceptionHandler();
}
}
public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
@Override
public void handleUncaughtException(Throwable throwable, Method method, Object... obj) {
System.out.println("异常捕获---------------------------------");
System.out.println("Exception message - " + throwable.getMessage());
System.out.println("Method name - " + method.getName());
for (Object param : obj) {
System.out.println("Parameter value - " + param);
}
System.out.println("异常捕获---------------------------------");
}
}Getting return values from async methods
Use Future or its subclasses, such as CompletableFuture, to receive results.
Void async methods throwing exceptions do not affect the main controller logic.
Async methods with return values that throw exceptions will affect the controller.
@Async
public CompletableFuture<Integer> updateReadCountHasResult() {
// TODO simulate time‑consuming operation
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("更新文章阅读量线程" + Thread.currentThread().getName());
return CompletableFuture.completedFuture(100 + 1);
}
// Controller call
@GetMapping("/article")
public String getArticle() throws ExecutionException, InterruptedException {
// Query article
String article = articleService.selectArticle();
// Increment read count
CompletableFuture<Integer> future = articleService.updateReadCountHasResult();
int count = 0;
// Wait for async result
while (true) {
if (future.isCancelled()) {
System.out.println("异步任务取消");
break;
}
if (future.isDone()) {
count = future.get();
System.out.println(count);
break;
}
}
System.out.println("文章阅读业务执行完毕");
return article + count;
}Issues and extensions of async methods
Async methods must be declared in a Service and called from a Controller; calling from another Service does not work.
Combining async with transactions may require separating them, with transaction logic executed before async.
Failure of an async method does not impact the preceding synchronous operations, making it unsuitable for strongly consistent requirements.
Message middleware such as RabbitMQ or Kafka can be more powerful for certain scenarios.
Java Interview Crash Guide
Dedicated to sharing Java interview Q&A; follow and reply "java" to receive a free premium Java interview guide.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
