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