Mastering Asynchronous Execution in Spring Boot: An In‑Depth Guide

This article provides a comprehensive guide to implementing asynchronous processing in Spring Boot, covering annotation‑based @Async, CompletableFuture patterns, WebAsyncTask, DeferredResult, custom thread pools, Tomcat tuning, component scanning, and switching to Undertow, with complete code examples and configuration details.

Java Architect Essentials
Java Architect Essentials
Java Architect Essentials
Mastering Asynchronous Execution in Spring Boot: An In‑Depth Guide

1. Asynchronous Execution

Spring Boot supports asynchronous processing through two main approaches: using the @Async annotation (combined with @EnableAsync on a configuration class) and leveraging the JDK 8 CompletableFuture API.

@AllArgsConstructor
public class AskThread implements Runnable {
    private CompletableFuture<Integer> re = null;
    public void run() {
        int myRe = 0;
        try {
            myRe = re.get() * re.get();
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(myRe);
    }
    public static void main(String[] args) throws InterruptedException {
        final CompletableFuture<Integer> future = new CompletableFuture<>();
        new Thread(new AskThread(future)).start();
        // Simulate a long‑running calculation
        Thread.sleep(1000);
        // Notify the result
        future.complete(60);
    }
}

The example starts a thread that blocks on re.get() until the result is supplied. A Thread.sleep(1000) simulates a time‑consuming operation, after which the result is completed and the thread proceeds.

public class Calc {
    public static Integer calc(Integer para) {
        try {
            // Simulate a long‑running execution
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return para * para;
    }
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        final CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> calc(50))
            .thenApply(i -> Integer.toString(i))
            .thenApply(str -> "\"" + str + "\"")
            .thenAccept(System.out::println);
        future.get();
    }
}

Here CompletableFuture.supplyAsync runs calc(50) in a separate thread, transforms the result, and prints it without blocking the main thread.

2. Using runAsync and Callable

CompletableFuture.runAsync(() -> this.afterBetProcessor(betRequest, betDetailResult, appUser, id));

When a controller returns a Callable, Spring MVC hands it to a TaskExecutor. The original request thread releases, while the callable runs in the common ForkJoinPool daemon pool.

@RestController
public class HelloController {
    private static final Logger logger = LoggerFactory.getLogger(HelloController.class);
    @Autowired
    private HelloService hello;

    @GetMapping("/helloworld")
    public String helloWorldController() {
        return hello.sayHello();
    }

    @GetMapping("/hello")
    public Callable<String> helloController() {
        logger.info(Thread.currentThread().getName() + " 进入helloController方法");
        Callable<String> callable = new Callable<String>() {
            @Override
            public String call() throws Exception {
                logger.info(Thread.currentThread().getName() + " 进入call方法");
                String say = hello.sayHello();
                logger.info(Thread.currentThread().getName() + " 从helloService方法返回");
                return say;
            }
        };
        logger.info(Thread.currentThread().getName() + " 从helloController方法返回");
        return callable;
    }
}

3. WebAsyncTask with Timeout Handling

@RestController
public class HelloController {
    private static final Logger logger = LoggerFactory.getLogger(HelloController.class);
    @Autowired
    private HelloService hello;

    @GetMapping("/world")
    public WebAsyncTask<String> worldController() {
        logger.info(Thread.currentThread().getName() + " 进入helloController方法");
        WebAsyncTask<String> webAsyncTask = new WebAsyncTask<>(3000, () -> {
            logger.info(Thread.currentThread().getName() + " 进入call方法");
            String say = hello.sayHello();
            logger.info(Thread.currentThread().getName() + " 从helloService方法返回");
            return say;
        });
        webAsyncTask.onCompletion(() -> logger.info(Thread.currentThread().getName() + " 完成"));
        webAsyncTask.onTimeout(() -> {
            logger.info(Thread.currentThread().getName() + " onTimeout");
            throw new TimeoutException("调用超时");
        });
        return webAsyncTask;
    }

    @GetMapping("/exception")
    public WebAsyncTask<String> exceptionController() {
        Callable<String> callable = () -> {
            logger.info(Thread.currentThread().getName() + " 进入call方法");
            throw new TimeoutException("调用超时!");
        };
        return new WebAsyncTask<>(20000, callable);
    }
}

4. Tuning Embedded Tomcat

@Configuration
public class TomcatConfig {
    @Bean
    public ConfigurableServletWebServerFactory webServerFactory() {
        TomcatServletWebServerFactory tomcatFactory = new TomcatServletWebServerFactory();
        tomcatFactory.addConnectorCustomizers(new MyTomcatConnectorCustomizer());
        tomcatFactory.setPort(8005);
        tomcatFactory.setContextPath("/api-g");
        return tomcatFactory;
    }
    class MyTomcatConnectorCustomizer implements TomcatConnectorCustomizer {
        @Override
        public void customize(Connector connector) {
            Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
            // Set max connections and threads
            protocol.setMaxConnections(20000);
            protocol.setMaxThreads(2000);
            protocol.setConnectionTimeout(30000);
        }
    }
}

5. Component Scan vs. @SpringBootApplication

Using @ComponentScan directly can be marginally faster than relying on the default component scan performed by @SpringBootApplication, because it avoids scanning the auto‑configuration packages.

6. Switching the Embedded Container to Undertow

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-undertow</artifactId>
</dependency>

Undertow typically offers higher throughput (e.g., 8000 requests/s) compared with Tomcat (≈5000 requests/s) in similar environments.

7. BufferedWriter for Efficient I/O

When writing large amounts of data, wrap the output stream with a BufferedWriter to reduce the number of physical writes.

8. DeferredResult for Long‑Running Requests

@RestController
public class AsyncDeferredController {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    private final LongTimeTask taskService;
    @Autowired
    public AsyncDeferredController(LongTimeTask taskService) {
        this.taskService = taskService;
    }
    @GetMapping("/deferred")
    public DeferredResult<String> executeSlowTask() {
        logger.info(Thread.currentThread().getName() + " 进入executeSlowTask方法");
        DeferredResult<String> deferredResult = new DeferredResult<>();
        // Submit the long‑running task
        taskService.execute(deferredResult);
        logger.info(Thread.currentThread().getName() + " 从executeSlowTask方法返回");
        // Timeout handling
        deferredResult.onTimeout(() -> {
            logger.info(Thread.currentThread().getName() + " onTimeout");
            deferredResult.setErrorResult("time out!");
        });
        // Completion handling (both success and timeout)
        deferredResult.onCompletion(() -> logger.info(Thread.currentThread().getName() + " onCompletion"));
        return deferredResult;
    }
}

9. AsyncHandlerInterceptor for Intercepting Asynchronous Calls

@Component
public class MyAsyncHandlerInterceptor implements AsyncHandlerInterceptor {
    private static final Logger logger = LoggerFactory.getLogger(MyAsyncHandlerInterceptor.class);
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        logger.info(Thread.currentThread().getName() + " 服务调用完成,返回结果给客户端");
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        if (ex != null) {
            System.out.println("发生异常:" + ex.getMessage());
        }
    }
    @Override
    public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // Replace the response body
        String resp = "my name is chhliu!";
        response.setContentLength(resp.length());
        response.getOutputStream().write(resp.getBytes());
        logger.info(Thread.currentThread().getName() + " 进入afterConcurrentHandlingStarted方法");
    }
}

The article concludes with a brief reference list of the source blogs.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

AsynchronousCompletableFutureSpring BootTomcatundertowDeferredResultWebAsyncTask
Java Architect Essentials
Written by

Java Architect Essentials

Committed to sharing quality articles and tutorials to help Java programmers progress from junior to mid-level to senior architect. We curate high-quality learning resources, interview questions, videos, and projects from across the internet to help you systematically improve your Java architecture skills. Follow and reply '1024' to get Java programming resources. Learn together, grow together.

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.