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.

Java Interview Crash Guide
Java Interview Crash Guide
Java Interview Crash Guide
Boost SpringBoot API Speed with Asynchronous Methods

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.

backendJavaThreadPoolSpringBootAsync
Java Interview Crash Guide
Written by

Java Interview Crash Guide

Dedicated to sharing Java interview Q&A; follow and reply "java" to receive a free premium Java interview guide.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.