4 Elegant Ways to Transfer Data Between Async Threads in Spring Boot

The article compares four techniques—manual ThreadLocal copying, TaskDecorator, InheritableThreadLocal, and Alibaba's TransmittableThreadLocal—for passing user context and other data from parent to child threads in Spring Boot async execution, recommending the TaskDecorator and TransmittableThreadLocal approaches for production use.

Programmer XiaoFu
Programmer XiaoFu
Programmer XiaoFu
4 Elegant Ways to Transfer Data Between Async Threads in Spring Boot

1. Manual Setting

Each asynchronous task must perform two steps: retrieve the parent thread's LoginVal from OauthContext and set it into the child thread before execution. Example:

public void handlerAsync() {
    // 1. Get parent thread's loginVal
    LoginVal loginVal = OauthContext.get();
    log.info("父线程的值:{}", OauthContext.get());
    CompletableFuture.runAsync(() -> {
        // 2. Set child thread's value, reuse
        OauthContext.set(loginVal);
        log.info("子线程的值:{}", OauthContext.get());
    });
}

While functional, this approach requires repetitive boiler‑plate code for every async call.

2. ThreadPool TaskDecorator

TaskDecorator

is an official Spring API that decorates task execution, allowing context propagation or monitoring. Implement it as follows:

/**
 * @description 上下文装饰器
 */
public class ContextTaskDecorator implements TaskDecorator {
    @Override
    public Runnable decorate(Runnable runnable) {
        // Capture parent thread's loginVal
        LoginVal loginVal = OauthContext.get();
        return () -> {
            try {
                // Transfer parent request info to child thread
                OauthContext.set(loginVal);
                // Execute child thread logic
                runnable.run();
            } finally {
                // Clear to avoid memory leaks
                OauthContext.clear();
            }
        };
    }
}

Configure the thread pool to use this decorator:

@Bean("taskExecutor")
public ThreadPoolTaskExecutor taskExecutor() {
    ThreadPoolTaskExecutor poolTaskExecutor = new ThreadPoolTaskExecutor();
    poolTaskExecutor.setCorePoolSize(xx);
    poolTaskExecutor.setMaxPoolSize(xx);
    poolTaskExecutor.setKeepAliveSeconds(xx);
    poolTaskExecutor.setQueueCapacity(xx);
    poolTaskExecutor.setTaskDecorator(new ContextTaskDecorator());
    poolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    poolTaskExecutor.setWaitForTasksToCompleteOnShutdown(true);
    return poolTaskExecutor;
}

Business code no longer needs to set the child thread's context manually:

public void handlerAsync() {
    log.info("父线程的用户信息:{}", OauthContext.get());
    // Execute async task with the configured pool
    CompletableFuture.runAsync(() ->
        log.info("子线程的用户信息:{}", OauthContext.get()), taskExecutor);
}

Result screenshot (omitted). The same mechanism works with @Async annotation.

Note: Regardless of the method used, a thread pool must be specified.

3. InheritableThreadLocal

Replacing ThreadLocal with InheritableThreadLocal enables parent‑child value inheritance, but it suffers from reuse problems when used with thread pools, so it is not recommended. Example:

public class OauthContext {
    private static final InheritableThreadLocal<LoginVal> loginValThreadLocal = new InheritableThreadLocal<>();
    public static LoginVal get() { return loginValThreadLocal.get(); }
    public static void set(LoginVal loginVal) { loginValThreadLocal.set(loginVal); }
    public static void clear() { loginValThreadLocal.remove(); }
}

4. TransmittableThreadLocal

Alibaba's open‑source TransmittableThreadLocal (TTL) extends InheritableThreadLocal and implements TtlCopier to correctly propagate ThreadLocal values across pooled threads. Add the dependency:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>transmittable-thread-local</artifactId>
    <version>2.14.2</version>
</dependency>

Modify the context class:

public class OauthContext {
    private static final TransmittableThreadLocal<LoginVal> loginValThreadLocal = new TransmittableThreadLocal<>();
    public static LoginVal get() { return loginValThreadLocal.get(); }
    public static void set(LoginVal loginVal) { loginValThreadLocal.set(loginVal); }
    public static void clear() { loginValThreadLocal.remove(); }
}

TTL works by capturing the current thread's values, wrapping tasks, and restoring the original values after execution. Core classes include TransmittableThreadLocal, TtlCallable, TtlRunnable, and utility Transmitter. The capture‑replay‑restore flow is roughly:

public class Transmitter {
    public static Object capture() { return new Snapshot(captureTtlValues(), captureThreadLocalValues()); }
    public static Object replay(Object captured) { /* replace child thread values */ }
    public static void restore(Object backup) { /* restore original values */ }
}

When a task is submitted via ExecutorService, TTL wraps it with TtlCallable (or TtlRunnable) which calls capture() in the parent, replaces the child thread's context in call(), executes the original callable, then restores the previous context.

Thus, using TTL ensures that any ThreadLocal (including those registered with TTL) is safely transmitted to pooled threads without the memory‑leak risks of InheritableThreadLocal.

Conclusion

The article lists four solutions and recommends approaches 2 (TaskDecorator) and 4 (TransmittableThreadLocal) as the most practical for real‑world Spring Boot asynchronous development.

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.

JavaConcurrencySpring BootthreadlocalAsynctransmittablethreadlocaltaskdecorator
Programmer XiaoFu
Written by

Programmer XiaoFu

xiaofucode.com – a programmer learning guide driven by the pursuit of profit

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.