Master AsyncTask Orchestration in Spring Boot with asyncTool
This guide explains how to integrate the asyncTool library into a Spring Boot project, configure custom thread pools, understand core interfaces like IWorker and ICallback, and use the provided Builder API to define serial, parallel, and mixed task flows with detailed code examples and best‑practice cautions.
Integration with Spring Boot
Add the asyncTool dependency to pom.xml:
<dependency>
<groupId>com.jd.platform</groupId>
<artifactId>asyncTool</artifactId>
<version>YOUR_VERSION</version>
</dependency>Although asyncTool manages its own thread pool, you can provide a custom pool for finer control. Two configuration approaches are available.
Custom thread pool
@Configuration
@EnableAsync // enable async execution
public class TaskExecutePool {
@Autowired
private TaskThreadPoolConfig config;
@Bean
public Executor myTaskAsyncPool() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(config.getCorePoolSize()); // core size
executor.setMaxPoolSize(config.getMaxPoolSize()); // max size
executor.setQueueCapacity(config.getQueueCapacity()); // queue capacity
executor.setKeepAliveSeconds(config.getKeepAliveSeconds()); // keep‑alive time
executor.setThreadNamePrefix("MyExecutor-"); // thread name prefix
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // rejection policy
executor.initialize();
return executor;
}
}Modify native Spring async pool
@Configuration
@EnableAsync // enable async execution
public class NativeAsyncTaskExecutePool implements AsyncConfigurer {
@Autowired
private TaskThreadPoolConfig config;
@Bean
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(config.getCorePoolSize());
executor.setMaxPoolSize(config.getMaxPoolSize());
executor.setQueueCapacity(config.getQueueCapacity());
executor.setKeepAliveSeconds(config.getKeepAliveSeconds());
executor.setThreadNamePrefix("MyExecutor2-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return (ex, method, objects) -> {
log.error("==========================" + ex.getMessage() + "=======================", ex);
log.error("exception method:" + method.getName());
};
}
}Core API
IWorker interface
action(T object, Map<String, WorkerWrapper> allWrappers): business logic; object is the input, allWrappers provides access to other tasks' results. defaultValue(): value returned when the task times out or throws an exception.
ICallback interface
begin(): invoked when the task starts.
result(boolean success, T param, WorkResult<V> workResult): receives the execution outcome; success indicates success, param is the input, and workResult holds the result.
WorkerWrapper class
id: unique identifier of the task. param: input parameter. worker: concrete implementation of the task. callback: callback implementation. depend: defines strong dependencies to enforce execution order. next: defines subsequent tasks for flexible sequencing.
Usage examples
Serial tasks
// Define task A
WorkerWrapper wrapperA = new WorkerWrapper.Builder<Integer, Integer>()
.id("workerA")
.worker(new WorkerA())
.callback(new WorkerA())
.param(1)
.build();
// Define task B, depends on A
WorkerWrapper wrapperB = new WorkerWrapper.Builder<Integer, Integer>()
.id("workerB")
.worker(new WorkerB())
.callback(new WorkerB())
.param(2)
.depend(wrapperA)
.build();
// Define task C, depends on B
WorkerWrapper wrapperC = new WorkerWrapper.Builder<Integer, Integer>()
.id("workerC")
.worker(new WorkerC())
.callback(new WorkerC())
.param(3)
.depend(wrapperB)
.build();
// Submit the first task (the chain will follow)
Async.beginWork(1000, wrapperA);Parallel tasks
// Define task A
WorkerWrapper wrapperA = new WorkerWrapper.Builder<Integer, Integer>()
.id("workerA")
.worker(new WorkerA())
.callback(new WorkerA())
.param(1)
.build();
// Define task B
WorkerWrapper wrapperB = new WorkerWrapper.Builder<Integer, Integer>()
.id("workerB")
.worker(new WorkerB())
.callback(new WorkerB())
.param(2)
.build();
// Define task C
WorkerWrapper wrapperC = new WorkerWrapper.Builder<Integer, Integer>()
.id("workerC")
.worker(new WorkerC())
.callback(new WorkerC())
.param(3)
.build();
// Submit all tasks together
Async.beginWork(1000, wrapperA, wrapperB, wrapperC);Block‑wait – serial then parallel
// Define task A
WorkerWrapper wrapperA = new WorkerWrapper.Builder<Integer, Integer>()
.id("workerA")
.worker(new WorkerA())
.callback(new WorkerA())
.param(1)
.build();
// Define task B, depends on A
WorkerWrapper wrapperB = new WorkerWrapper.Builder<Integer, Integer>()
.id("workerB")
.worker(new WorkerB())
.callback(new WorkerB())
.param(2)
.depend(wrapperA)
.build();
// Define task C, also depends on A
WorkerWrapper wrapperC = new WorkerWrapper.Builder<Integer, Integer>()
.id("workerC")
.worker(new WorkerC())
.callback(new WorkerC())
.param(3)
.depend(wrapperA)
.build();
Async.beginWork(1000, wrapperA);Block‑wait – parallel then serial
// Define task A (receives results of B and C)
WorkerWrapper wrapperA = new WorkerWrapper.Builder<Integer, Integer>()
.id("workerA")
.worker(new WorkerA())
.callback(new WorkerA())
.param(null)
.build();
// Define task B
WorkerWrapper wrapperB = new WorkerWrapper.Builder<Integer, Integer>()
.id("workerB")
.worker(new WorkerB())
.callback(new WorkerB())
.param(2)
.next(wrapperA)
.build();
// Define task C
WorkerWrapper wrapperC = new WorkerWrapper.Builder<Integer, Integer>()
.id("workerC")
.worker(new WorkerC())
.callback(new WorkerC())
.param(3)
.next(wrapperA)
.build();
Async.beginWork(1000, wrapperB, wrapperC);Key benefits
Task orchestration : combine any number of serial and parallel tasks to match business requirements.
Dependency management : support strong and weak dependencies, allowing tasks to wait for specific predecessors.
Execution monitoring & callbacks : each task triggers callbacks on start, success, failure, timeout, or exception, enabling real‑time monitoring.
Exception handling & fault tolerance : configurable timeout and default return values keep the overall chain stable; a failed upstream task propagates failure to dependent tasks.
Performance optimization : low‑thread design reduces thread creation overhead; the framework is lock‑free for higher concurrency.
Result management : results can be returned in the order tasks were added, and the whole task group also supports asynchronous callbacks.
Thread‑pool management : each task group can use a dedicated pool or share a common pool, offering flexible resource allocation.
Simplified development : complex concurrency logic is encapsulated, letting developers focus on business code.
Precautions
Ensure task implementations are thread‑safe because they may run on multiple threads.
Handle exceptions inside tasks to avoid cascading failures.
Set reasonable timeout values to prevent resource waste.
Configure dependency relationships correctly so tasks execute in the intended order.
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 Architect Handbook
Focused on Java interview questions and practical article sharing, covering algorithms, databases, Spring Boot, microservices, high concurrency, JVM, Docker containers, and ELK-related knowledge. Looking forward to progressing together with you.
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.
