Master Asynchronous Execution in Spring Boot: @Async, CompletableFuture, WebAsyncTask & More
This article explores multiple ways to implement asynchronous processing in Spring Boot, covering @Async with @EnableAsync, CompletableFuture, DeferredResult, WebAsyncTask, AsyncHandlerInterceptor, as well as related server tweaks like increasing Tomcat connections, switching to Undertow, and using @ComponentScan for faster scanning.
Asynchronous Execution
Two implementation methods are presented: using the asynchronous annotation @Async together with @EnableAsync, and leveraging JDK 8's CompletableFuture.
@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 computation
Thread.sleep(1000);
// Complete the future with the result
future.complete(60);
}
}In this example a thread is started before the AskThread receives its data; the call to myRe = re.get() * re.get() blocks until the future is completed.
Another example demonstrates using CompletableFuture.supplyAsync to run a slow calculation without blocking the caller.
public class Calc {
public static Integer calc(Integer para) {
try {
// Simulate a long 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();
}
} CompletableFuture.supplyAsynccreates a new thread to execute the supplied function, returning immediately; the resulting CompletableFuture acts as a contract for obtaining the final result later. For fire‑and‑forget tasks, CompletableFuture.runAsync(Runnable) is often used. Both methods run in the ForkJoinPool.common daemon thread pool, which terminates when the main thread ends.
Increasing Embedded Tomcat Max Connections
@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 {
public void customize(Connector connector) {
Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
// Set max connections
protocol.setMaxConnections(20000);
// Set max threads
protocol.setMaxThreads(2000);
protocol.setConnectionTimeout(30000);
}
}
}Using @ComponentScan() for Faster Scanning
Applying @ComponentScan() directly can locate packages faster than using @SpringBootApplication, which implicitly includes component scanning.
Switching Default Tomcat to Undertow
Replacing the default embedded Tomcat with Undertow improves throughput (approximately 8000 requests per second vs. 5000 for Tomcat).
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>Using BufferedWriter for Buffering
BufferedWriter can be used to buffer output streams; readers are encouraged to try it on their own.
Deferred Approach for Asynchronous Calls
@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<>();
// Invoke a long‑running task
taskService.execute(deferredResult);
logger.info(Thread.currentThread().getName() + "从executeSlowTask方法返回");
deferredResult.onTimeout(() -> {
logger.info(Thread.currentThread().getName() + " onTimeout");
deferredResult.setErrorResult("time out!");
});
deferredResult.onCompletion(() -> logger.info(Thread.currentThread().getName() + " onCompletion"));
return deferredResult;
}
}Async Calls with AsyncHandlerInterceptor
@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 {
String resp = "my name is chhliu!";
response.setContentLength(resp.length());
response.getOutputStream().write(resp.getBytes());
logger.info(Thread.currentThread().getName() + " afterConcurrentHandlingStarted方法");
}
}WebAsyncTask for Asynchronous Requests with Timeout
@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 = () -> {
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;
}
@GetMapping("/world")
public WebAsyncTask<String> worldController() {
logger.info(Thread.currentThread().getName() + " 进入helloController方法");
WebAsyncTask<String> webAsyncTask = new WebAsyncTask<>(3000, () -> hello.sayHello());
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() {
logger.info(Thread.currentThread().getName() + " 进入helloController方法");
Callable<String> callable = () -> {
logger.info(Thread.currentThread().getName() + " 进入call方法");
throw new TimeoutException("调用超时!");
};
logger.info(Thread.currentThread().getName() + " 从helloController方法返回");
return new WebAsyncTask<>(20000, callable);
}
}The image below illustrates the article’s focus on asynchronous techniques.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Backend Technology
Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!
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.
