Backend Development 13 min read

Elegant Ways to Transfer Data Between Parent and Child Threads in Spring Boot

This article introduces four methods—manual setting, TaskDecorator, InheritableThreadLocal, and TransmittableThreadLocal—to propagate contextual data such as user information from a parent thread to child threads in Spring Boot asynchronous execution, providing code examples and practical recommendations.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
Elegant Ways to Transfer Data Between Parent and Child Threads in Spring Boot

Spring Boot developers often need to pass contextual data (e.g., user info, trace IDs) from a parent thread to child threads when using asynchronous tasks. This guide presents four approaches to achieve that goal.

1. Manual Setting

Each asynchronous task requires two steps: retrieve the LoginVal from the parent thread and set it in the child thread. The following code demonstrates this pattern:

/**
 * @author 公众号:码猿技术专栏
 * @description 用户上下文信息
 */
public class OauthContext {
    private static final ThreadLocal
loginValThreadLocal = new ThreadLocal<>();
    public static LoginVal get() { return loginValThreadLocal.get(); }
    public static void set(LoginVal loginVal) { loginValThreadLocal.set(loginVal); }
    public static void clear() { loginValThreadLocal.remove(); }
}

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

While functional, this approach repeats boilerplate code for every async task.

2. ThreadPool TaskDecorator

The TaskDecorator interface allows you to wrap each task and automatically transfer context. Implement it as follows:

/**
 * @author 公众号:码猿技术专栏
 * @description 上下文装饰器
 */
public class ContextTaskDecorator implements TaskDecorator {
    @Override
    public Runnable decorate(Runnable runnable) {
        // Capture parent thread's value
        LoginVal loginVal = OauthContext.get();
        return () -> {
            try {
                // Set value in child thread
                OauthContext.set(loginVal);
                runnable.run();
            } finally {
                // Prevent memory leak
                OauthContext.clear();
            }
        };
    }
}

Configure the thread pool to use this decorator:

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

Business code no longer needs manual context setting:

public void handlerAsync() {
    log.info("父线程的用户信息:{}", OauthContext.get());
    CompletableFuture.runAsync(() ->
        log.info("子线程的用户信息:{}", OauthContext.get()), taskExecutor);
}

3. InheritableThreadLocal

Replacing ThreadLocal with InheritableThreadLocal enables automatic inheritance, but it can cause stale data when threads are pooled. The implementation is straightforward:

public class OauthContext {
    private static final InheritableThreadLocal
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 (TTL)

TTL, an Alibaba open‑source library, extends InheritableThreadLocal to work correctly with thread pools. Add the dependency:

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

Replace the context implementation with TransmittableThreadLocal :

public class OauthContext {
    private static final TransmittableThreadLocal
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 (both TTL and regular ThreadLocal ) into a snapshot, replaying them in the worker thread before execution, and restoring the original values afterward. Core classes such as Transmitter , TtlCallable , and TtlRunnable implement this logic.

Summary

The article compares four techniques for propagating parent‑thread context to child threads in Spring Boot. It recommends using either the TaskDecorator approach (method 2) or the TTL library (method 4) for production‑grade solutions, as they avoid the boilerplate of manual setting and the pitfalls of InheritableThreadLocal .

JavaSpring BootAsynchronous ProgrammingthreadlocalTransmittableThreadLocalTaskDecorator
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

login 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.