Using Asynchronous Threads in Spring Boot: @Async, AsyncManager, and ThreadPoolExecutor
This article explains how to implement asynchronous processing in Java Spring Boot using the @Async annotation, the built‑in AsyncManager, and a custom ThreadPoolExecutor, providing code examples, configuration steps, and best‑practice tips for managing background tasks without blocking the main thread.
Overall Description
In Java, asynchronous threads are important for time‑consuming operations such as notifying hardware, sending SMS, or uploading images, because handling them in the main thread would block the business flow; using async threads keeps the main thread free.
Recent project experience with many thread operations is recorded here.
Implementation Methods
There are several common ways to work with threads in Spring Boot: the @Async annotation, the built‑in AsyncManager, and a custom thread pool.
1. @Async Annotation
The @Async annotation cannot be called directly on the class itself; it should be placed on a method in a separate @Service and invoked from another component.
1. Add @EnableAsync
Enable async support in a configuration class:
package com.thcb.boot.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
/**
* MyConfig
*
* @author thcb
*/
@Configuration
@EnableAsync
public class MyConfig {
// custom config
}2. Create an async service
Define an interface with an @Async method and its implementation:
package com.thcb.execute.service;
import org.springframework.scheduling.annotation.Async;
public interface IExecuteService {
/**
* Simulated time‑consuming operation
*/
@Async
void sleepingTest();
} package com.thcb.execute.service.impl;
import com.thcb.execute.service.IExecuteService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@Service
public class ExecuteServiceImpl implements IExecuteService {
private static final Logger log = LoggerFactory.getLogger(ExecuteServiceImpl.class);
@Override
public void sleepingTest() {
log.info("SleepingTest start");
try {
Thread.sleep(5000);
} catch (Exception e) {
log.error("SleepingTest:" + e);
}
log.info("SleepingTest end");
}
}2. AsyncManager
AsyncManager is a Spring‑Boot task manager that can schedule TimerTask instances.
1. Create AsyncManager class
public class AsyncManager {
/** operation delay 10 ms */
private final int OPERATE_DELAY_TIME = 10;
/** thread pool */
private ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService");
/** singleton */
private AsyncManager() {}
private static final AsyncManager me = new AsyncManager();
public static AsyncManager me() { return me; }
/** execute task */
public void execute(TimerTask task) {
executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS);
}
/** shutdown */
public void shutdown() {
Threads.shutdownAndAwaitTermination(executor);
}
}2. Create a time‑consuming TimerTask
public TimerTask sleepingTest() {
return new TimerTask() {
@Override
public void run() {
// time‑consuming operation
try {
Thread.sleep(5000);
} catch (Exception e) {
log.error("SleepingTest:" + e);
}
}
};
}3. Execute with AsyncManager
// async thread pool
AsyncManager.me().execute(sleepingTest());3. ThreadPoolExecutor
A custom thread pool with a rejection policy allows fine‑grained control over core size, max size, queue length and keep‑alive time.
1. Create thread pool
private static final ThreadPoolExecutor threadPoolExecutor =
new ThreadPoolExecutor(5, 10, 30, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(20),
new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
log.error("Task rejected");
}
});2. Define a Runnable task
static class MyTask implements Runnable {
private int taskNum;
public MyTask(int num) { this.taskNum = num; }
@Override
public void run() {
System.out.println("Executing task " + taskNum);
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("task " + taskNum + " completed");
}
}3. Submit tasks
for (int i = 0; i < 20; i++) {
MyTask myTask = new MyTask(i);
threadPoolExecutor.execute(myTask);
System.out.println("Pool size: " + threadPoolExecutor.getPoolSize()
+ ", queue size: " + threadPoolExecutor.getQueue().size()
+ ", completed: " + threadPoolExecutor.getCompletedTaskCount());
}
threadPoolExecutor.shutdown();Summary
Thread count should match CPU cores; always release threads to avoid exhaustion.
@Async methods must be placed in a separate @Service because Spring creates a proxy.
Define a single static final thread pool and reuse it instead of creating new instances each time.
Java 8 also introduced CompletableFuture, which can be covered in a separate article.
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.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
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.
