Using Alibaba's TransmittableThreadLocal to Propagate ThreadLocal Variables in Thread Pools

This article explains why InheritableThreadLocal fails in pooled threads, introduces Alibaba's TransmittableThreadLocal library, demonstrates how to wrap executors and rewrite code to correctly transmit ThreadLocal data across parent‑child threads in Java thread pools, and provides detailed implementation and usage examples.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
Using Alibaba's TransmittableThreadLocal to Propagate ThreadLocal Variables in Thread Pools

When asynchronous calls lose context information, the root cause is usually ThreadLocal, which cannot inherit values across pooled threads. InheritableThreadLocal works only for newly created child threads, not for threads reused by an ExecutorService.

Alibaba's TransmittableThreadLocal (TTL) extends InheritableThreadLocal and adds a mechanism to capture, transmit, and restore ThreadLocal values even when threads are reused. It integrates with executors via the TtlExecutors wrapper, which returns a wrapped ExecutorService that creates TtlCallable or TtlRunnable tasks.

Example of the problem with InheritableThreadLocal:

@Test
public void test() throws Exception {
    ExecutorService executorService = Executors.newSingleThreadExecutor();
    InheritableThreadLocal<String> username = new InheritableThreadLocal<>();
    for (int i = 0; i < 10; i++) {
        username.set("公众号:码猿技术专栏—" + i);
        Thread.sleep(3000);
        CompletableFuture.runAsync(() -> System.out.println(username.get()), executorService);
    }
}

The output shows the same value repeated because the thread is reused and the parent value is not copied.

After adding TTL:

@Test
public void test() throws Exception {
    ExecutorService executorService = Executors.newSingleThreadExecutor();
    executorService = TtlExecutors.getTtlExecutorService(executorService);
    TransmittableThreadLocal<String> username = new TransmittableThreadLocal<>();
    for (int i = 0; i < 10; i++) {
        username.set("公众号:码猿技术专栏—" + i);
        Thread.sleep(3000);
        CompletableFuture.runAsync(() -> System.out.println(username.get()), executorService);
    }
}

Now each asynchronous task prints the correct incremental value, demonstrating successful context propagation.

TTL works by capturing the current thread's TTL and regular ThreadLocal values into a snapshot, replaying them in the worker thread before execution, and restoring the original values afterward. The core classes involved are Transmitter, Snapshot, TtlCallable, and TtlRunnable.

Typical usage in a Spring Security interceptor stores the authenticated user in a static TransmittableThreadLocal and clears it after request completion, ensuring the user context is available in any async processing triggered by the request.

In summary, TransmittableThreadLocal provides an elegant solution for passing ThreadLocal data across parent‑child threads in pooled environments, a common requirement in distributed tracing, logging, and security contexts.

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.

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