Mastering AsyncTask Orchestration in Spring Boot with asyncTool

This guide shows how to integrate the asyncTool library into a Spring Boot project, configure custom thread pools, understand its core interfaces, and use code examples to build serial, parallel, and mixed task workflows with full monitoring, fault tolerance, and performance optimizations.

Java Tech Enthusiast
Java Tech Enthusiast
Java Tech Enthusiast
Mastering AsyncTask Orchestration in Spring Boot with asyncTool

Integration into Spring Boot

1. Add Dependency

Add the asyncTool dependency to the project's pom.xml:

<dependency>
    <groupId>com.jd.platform</groupId>
    <artifactId>asyncTool</artifactId>
    <version>{version}</version>
</dependency>

2. Configure Thread Pool

asyncTool manages an internal pool, but you can supply a custom Executor for finer control. Two approaches are shown.

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 Thread 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 Interfaces

IWorker Interface

action(T object, Map<String, WorkerWrapper> allWrappers)

: core task 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()

: called when the task starts.

result(boolean success, T param, WorkResult<V> workResult)

: callback with execution outcome; success indicates success, param is the input, 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 or weak dependencies to control execution order. next: defines subsequent tasks.

Detailed Usage and Examples

Serial Tasks

Tasks are executed one after another in the defined order.

// Task A
WorkerWrapper wrapperA = new WorkerWrapper.Builder<Integer, Integer>()
        .id("workerA")
        .worker(new WorkerA())
        .callback(new WorkerA())
        .param(1)
        .build();

// 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();

// 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();

Async.beginWork(1000, wrapperA);

Parallel Tasks

Multiple tasks run concurrently.

// Task A
WorkerWrapper wrapperA = new WorkerWrapper.Builder<Integer, Integer>()
        .id("workerA")
        .worker(new WorkerA())
        .callback(new WorkerA())
        .param(1)
        .build();

// Task B
WorkerWrapper wrapperB = new WorkerWrapper.Builder<Integer, Integer>()
        .id("workerB")
        .worker(new WorkerB())
        .callback(new WorkerB())
        .param(2)
        .build();

// Task C
WorkerWrapper wrapperC = new WorkerWrapper.Builder<Integer, Integer>()
        .id("workerC")
        .worker(new WorkerC())
        .callback(new WorkerC())
        .param(3)
        .build();

Async.beginWork(1000, wrapperA, wrapperB, wrapperC);

Blocking Wait – Serial then Parallel

Execute task A first, then run B and C in parallel.

// Task A
WorkerWrapper wrapperA = new WorkerWrapper.Builder<Integer, Integer>()
        .id("workerA")
        .worker(new WorkerA())
        .callback(new WorkerA())
        .param(1)
        .build();

// 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();

// Task C 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);

Blocking Wait – Parallel then Serial

Run B and C in parallel first, then execute A after they finish.

// 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) // will receive results of B and C
        .build();

// Task B
WorkerWrapper wrapperB = new WorkerWrapper.Builder<Integer, Integer>()
        .id("workerB")
        .worker(new WorkerB())
        .callback(new WorkerB())
        .param(2)
        .next(wrapperA)
        .build();

// 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);

Main Features

Task Orchestration

Supports arbitrary combinations of parallel and serial tasks, allowing developers to define execution order based on business needs.

Execution Monitoring and Callbacks

Every task triggers a callback regardless of success, failure, timeout, or exception, enabling real‑time monitoring. Skipped tasks also invoke callbacks for logging or error handling.

Exception Handling and Fault Tolerance

Each task can specify a timeout and a default return value; failures of independent tasks do not affect others, while upstream failures propagate to dependent tasks.

Performance Optimizations

Low‑thread design reduces thread creation overhead, and the framework is lock‑free, improving concurrency performance.

Result Management

After execution, results can be returned in the order tasks were added, and the whole task group also supports asynchronous callbacks to avoid blocking the main thread.

Thread Pool Management

Thread pools can be shared across all task groups or dedicated to a specific group, offering flexible resource allocation.

Simplified Development

asyncTool encapsulates complex concurrency logic, letting developers focus on business code without deep knowledge of low‑level threading mechanisms.

Precautions

Thread safety: Tasks may run on multiple threads concurrently; ensure they are thread‑safe.

Exception handling: Properly handle exceptions to avoid affecting the whole application.

Timeout configuration: Set reasonable timeouts to prevent resource waste.

Dependency configuration: Correctly define task dependencies to guarantee the intended execution order.

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.

Thread Poolasynctask orchestrationbackend-developmentspring-boot
Java Tech Enthusiast
Written by

Java Tech Enthusiast

Sharing computer programming language knowledge, focusing on Java fundamentals, data structures, related tools, Spring Cloud, IntelliJ IDEA... Book giveaways, red‑packet rewards and other perks await!

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.