📖 English Documentation | 📖 中文文档
👉 TransmittableThreadLocal(TTL): The missing Java™ std lib(simple & 0-dependency) for framework/middleware,
provide an enhanced InheritableThreadLocal that transmits values between threads even using thread pooling components. Support Java 6~21.
Class InheritableThreadLocal in JDK
can transmit value to child thread from parent thread.
But when use thread pool, thread is cached up and used repeatedly. Transmitting value from parent thread to child thread has no meaning. Application need transmit value from the time task is created to the time task is executed.
If you have problem or question, please submit Issue or play fork and pull request dance.
From
TTL v2.13+upgrade toJava 8.
If you needJava 6support, use version2.12.x
The Requirements listed below is also why I sort out TransmittableThreadLocal in my work.
TransmittableThreadLocal<String> context = new TransmittableThreadLocal<>();
// =====================================================
// set in parent thread
context.set("value-set-in-parent");
// =====================================================
// read in child thread, value is "value-set-in-parent"
String value = context.get();
# See the executable demo SimpleDemo.kt with full source code.
This is the function of class InheritableThreadLocal, should use class InheritableThreadLocal instead.
But when use thread pool, thread is cached up and used repeatedly. Transmitting value from parent thread to child thread has no meaning. Application need transmit value from the time task is created to the point task is executed.
The solution is below usage.
Runnable and CallableDecorate input Runnable and Callable by TtlRunnable
and TtlCallable.
Sample code:
TransmittableThreadLocal<String> context = new TransmittableThreadLocal<>();
// =====================================================
// set in parent thread
context.set("value-set-in-parent");
Runnable task = new RunnableTask();
// extra work, create decorated ttlRunnable object
Runnable ttlRunnable = TtlRunnable.get(task);
executorService.submit(ttlRunnable);
// =====================================================
// read in task, value is "value-set-in-parent"
String value = context.get();
NOTE:
Even when the same Runnable task is submitted to the thread pool multiple times, the decoration operations(ie: TtlRunnable.get(task)) is required for each submission to capture the value of the TransmittableThreadLocal context at submission time; That is, if the same task is submitted next time without reperforming decoration and still using the last TtlRunnable, the submitted task will run in the context of the last captured context. The sample code is as follows:
// first submission
Runnable task = new RunnableTask();
executorService.submit(TtlRunnable.get(task));
// ... some biz logic,
// and modified TransmittableThreadLocal context ...
context.set("value-modified-in-parent");
// next submission
// reperform decoration to transmit the modified TransmittableThreadLocal context
executorService.submit(TtlRunnable.get(task));
Above code show how to dealing with Runnable, Callable is similar:
TransmittableThreadLocal<String> context = new TransmittableThreadLocal<>();
// =====================================================
// set in parent thread
context.set("value-set-in-parent");
Callable call = new CallableTask();
// extra work, create decorated ttlCallable object
Callable ttlCallable = TtlCallable.get(call);
executorService.submit(ttlCallable);
// =====================================================
// read in call, value is "value-set-in-parent"
String value = context.get();
# See the executable demo TtlWrapperDemo.kt with full source code.
Eliminating the work of Runnable and Callable Decoration every time it is submitted to thread pool. This work can be completed in the thread pool.
Use util class
TtlExecutors
to decorate thread pool.
Util class TtlExecutors has below methods:
getTtlExecutor: decorate interface ExecutorgetTtlExecutorService: decorate interface ExecutorServicegetTtlScheduledExecutorService: decorate interface ScheduledExecutorServiceSample code:
ExecutorService executorService = ...
// extra work, create decorated executorService object
executorService = TtlExecutors.getTtlExecutorService(executorService);
TransmittableThreadLocal<String> context = new TransmittableThreadLocal<>();
// =====================================================
// set in parent thread
context.set("value-set-in-parent");
Runnable task = new RunnableTask();
Callable call = new CallableTask();
executorService.submit(task);
executorService.submit(call);
// =====================================================
// read in Task or Callable, value is "value-set-in-parent"
String value = context.get();
# See the executable demo TtlExecutorWrapperDemo.kt with full source code.
In this usage, transmittance is transparent(no decoration operation).
Sample code:
// ## 1. upper layer logic of framework ##
TransmittableThreadLocal<String> context = new TransmittableThreadLocal<>();
context.set("value-set-in-parent");
// ## 2. biz logic ##
ExecutorService executorService = Executors.newFixedThreadPool(3);
Runnable task = new RunnableTask();
Callable call = new CallableTask();
executorService.submit(task);
executorService.submit(call);
// ## 3. underlayer logic of framework ##
// read in Task or Callable, value is "value-set-in-parent"
String value = context.get();
# See the executable demo AgentDemo.kt with full source code, run demo by the script scripts/run-agent-demo.sh.
At present, TTL agent has decorated below JDK execution components(aka. thread pool) implementation:
java.util.concurrent.ThreadPoolExecutor and java.util.concurrent.ScheduledThreadPoolExecutorTtlExecutorTransformlet.java.java.util.concurrent.ForkJoinTask(corresponding execution component is java.util.concurrent.ForkJoinPool)TtlForkJoinTransformlet.java, supports since version 2.5.1.CompletableFuture and (parallel) Stream introduced in Java 8 is executed through ForkJoinPool underneath, so after supporting ForkJoinPool, TTL also supports CompletableFuture and Stream transparently. 🎉java.util.TimerTask(corresponding execution component is java.util.Timer)TtlTimerTaskTransformlet.java, supports since version 2.7.0.2.11.2 decoration for TimerTask default is enable (because correctness is first concern, not the best practice like "It is not recommended to use TimerTask" :); before version 2.11.1 default is disable.ttl.agent.enable.timer.task:-javaagent:path/to/transmittable-thread-local-2.x.y.jar=ttl.agent.enable.timer.task:true-javaagent:path/to/transmittable-thread-local-2.x.y.jar=ttl.agent.enable.timer.task:falseTTL agent arguments, see the javadoc of TtlAgent.java.Add start options on Java command:
-javaagent:path/to/transmittable-thread-local-2.x.y.jarJava command example:
```bash java -javaagent:transmittable-thread-local-2.x.y.jar \ -cp classes \ com.alibaba.demo.ttl.agent.
$ claude mcp add transmittable-thread-local \
-- python -m otcore.mcp_server <graph>