Mastering AsyncTask Orchestration in Spring Boot with asyncTool

Learn how to integrate asyncTool into a Spring Boot project, configure custom thread pools, understand core interfaces like IWorker and ICallback, and implement serial, parallel, and mixed task flows with detailed code examples and best‑practice guidelines for robust asynchronous task orchestration.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
Mastering AsyncTask Orchestration in Spring Boot with asyncTool

Integration into Spring Boot

1. Add Dependency

In the project's pom.xml add the asyncTool dependency:

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

2. Configure Thread Pool

Although asyncTool manages a thread pool internally, you can customize it. Two ways:

1) Custom Thread Pool

@Configuration
@EnableAsync
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
        executor.setThreadNamePrefix("MyExecutor-"); // name prefix
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // rejection policy
        executor.initialize();
        return executor;
    }
}

2) Modify Native Spring Async Executor

@Configuration
@EnableAsync
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 Method Description

1. IWorker Interface

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

: execution logic; object is input, allWrappers provides access to other task results. defaultValue(): default return value when timeout or exception occurs.

2. ICallback Interface

begin()

: called when task starts.

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

: called after execution; success indicates outcome, param is input, workResult holds result.

3. WorkerWrapper Class

id

: unique identifier. param: input parameter. worker: task implementation. callback: callback implementation. depend: defines dependent tasks (execution order). next: defines subsequent tasks.

Detailed Usage and Examples

1. Serial Tasks

Tasks execute sequentially. Example:

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

2. 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);

3. Blocking Wait – Serial then Parallel

Execute A first, then B and C in parallel:

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

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

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

4. Blocking Wait – Parallel then Serial

B and C run in parallel, then A runs after they finish:

// Task A will receive results of B and C
WorkerWrapper wrapperA = new WorkerWrapper.Builder<Integer, Integer>()
        .id("workerA")
        .worker(new WorkerA())
        .callback(new WorkerA())
        .param(null) // will receive B and C results
        .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

1. Task Orchestration

Flexible parallel and serial composition: asyncTool allows arbitrary combinations of parallel and serial tasks, letting developers define execution order per business needs.

Dependency management: Supports strong and weak dependencies, ensuring tasks run only after required predecessors finish.

2. Execution Monitoring and Callbacks

Full‑link callback mechanism: Every task triggers callbacks on success, failure, timeout, or exception, enabling real‑time monitoring.

Skipped‑task callbacks: Even tasks that are skipped generate callbacks for logging or error handling.

3. Exception Handling and Fault Tolerance

Exception and timeout handling: Each task can set a timeout and a default value; on failure or timeout the default is returned, keeping the chain stable.

Independent task fault tolerance: Failure of a single task does not affect other tasks’ callbacks or final results, unless upstream dependencies fail.

4. Performance Optimization

Low‑thread design: asyncTool reduces thread creation/destruction overhead; downstream tasks can reuse threads from upstream tasks.

Lock‑free architecture: The framework operates without locks, avoiding lock‑related performance penalties and improving concurrency.

5. Result Management

Ordered result return: After execution, asyncTool returns results in the order tasks were added, simplifying downstream processing.

Asynchronous callbacks: Besides synchronous blocking returns, the whole task group can provide asynchronous callbacks to avoid blocking the main thread.

6. Thread‑Pool Management

Shared vs. exclusive pools: You can allocate a dedicated thread pool per task group or share a common pool across groups.

7. Simplified Development

Encapsulated complex logic: asyncTool hides intricate concurrency details, allowing developers to focus on business logic.

Precautions

Thread safety: Ensure tasks are thread‑safe when executed concurrently.

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

Timeout configuration: Set reasonable timeouts to prevent resource waste.

Dependency configuration: Correctly define task dependencies to guarantee expected 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.

JavaSpring BootThread Pooltask orchestrationasyncTool
Code Ape Tech Column
Written by

Code Ape Tech Column

Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn

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.