阿里TTL(TransmittableThreadLocal)分析
阿里TTL(TransmittableThreadLocal)分析
GITHUB:alibaba/transmittable-thread-local
ThreadLocal系列(一)-ThreadLocal的使用及原理解析
继承关系:ThreadLocal<T> <- InheritableThreadLocal<T> <- TransmittableThreadLocal<T>

阿里的TTL是扩展自java的ThreadLocal体系,因此想要了解TTL的功能需要先对继承的java父类功能有了解。
下面分别讲讲 ThreadLocal 、 InheritableThreadLocal。然后再介绍 TransmittableThreadLocal。
ThreadLocal
1. 介绍
ThreadLocal 类提供线程本地(局部)变量。每个线程都有自己独立初始化的变量副本。
TheadLocal 允许我们存储仅由特定线程访问的数据
使用场景:
- 每个线程持有自己的数据变量,如:
/**
* 每个线程拥有自己的Integer变量,默认初始化为0
*/
private static final ThreadLocal<Integer> INTEGER_THREAD_LOCAL = ThreadLocal.withInitial(() -> 0);
- 避免数据竞争,每个线程持有自己的线程变量,如:
/**
* 每个线程拥有自己的SDF,避免竞争,保证线程安全
*/
private static final ThreadLocal<SimpleDateFormat> SDF_TL = ThreadLocal.withInitial(() -> {
return new SimpleDateFormat("yyyyMMddHHmmss");
});
- 数据传递,跨方法级别数据传递
/**
* 数据传递上下文: LogbackMDCAdapter MDC 实际功能实现
*/
final ThreadLocal<Map<String, String>> context = new ThreadLocal<>();
2. 方法
get()获取线程本地变量,如果没有,则调用initialValue()初始化并设置。
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
set()设置线程本地变量
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
remove()移除线程本地变量
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
3.实现原理
ThreadLocal实现主要依赖
1.ThreadLocal实例对象,作为线程访问数据的KEY存在。
2.ThreadLocalMap对象,每个线程有自己的map,ThreadLocal实例作为KEY。
注意:此处entry中的ThreadLocal key使用弱引用,防止内存泄漏,在清除方法中会清除所有 key为null的entry。
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
ThreadLoclMap作为每个线程实例的字段存储在线程实例中:
public class Thread implements Runnable {
/**
* 与此线程相关的ThreadLocal值。此Map由 ThreadLocal 类维护。
*/
ThreadLocal.ThreadLocalMap threadLocals = null;
/**
* 与此线程相关的 InheritableThreadLocal 值。此映射由 InheritableThreadLocal 类维护
*/
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
}
关系示意图:

InheritableThreadLocal
1. 介绍
public class InheritableThreadLocal<T> extends ThreadLocal<T>
InheritableThreadLocal 可继承的ThreadLocal,继承并扩扩展了原始ThreadLocal的功能,提供从父线程到子线程的value继承:创建子线程时,子线程接收父线程具有的 inheritable thread-local 值。
使用场景:
- 新建子现成需要集成父线程中的线程本地变量时,可以使用 InheritableThreadLocal 实现:
/**
* 可继承ThreadLocal,子线程将继承父线程中ITL变量value
*/
private static final ThreadLocal<Integer> BIZ_ITL = new InheritableThreadLocal();
/**
* 运行结果:
* main-thread-get:123
* sub-thread-get:123
*/
public static void main(String[] args) {
BIZ_ITL.set(123);
System.out.println("main-thread-get:"+BIZ_ITL.get());
new Thread(()->{
System.out.println("sub-thread-get:"+BIZ_ITL.get());
}).start();
}
2. 方法
getMap()createMap()使用ITLMap,非 TLMap。
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
3.实现原理
子程继承父线程 现成本地变量值 是在Thread创建时,copy父类现成中的 ITLMap中的key和Value:
Thread构造函数:调用init方法,在init方法中子线程根据父线程的ITLMap创建自己的ITLMap(传递、继承)。
需要注意:这里子现成中的value和父线程中为同一个对象。
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
}
3. TransmittableThreadLocal 组件
1.介绍
ThreadLocal和InheritableThreadLocal 能够完成变量的线程本地化和父子线程中的value传递。
但是现实项目中大多数线程池化在线程池中,因此,提交任务的线程无法将 提交现成的本地变量传递给执行task的任务线程。
TTL组件功能:在使用线程池等会池化复用线程的执行组件情况下,提供ThreadLocal值的传递功能,解决异步执行时上下文传递的问题。
业务期望:上下文生命周期的操作从业务逻辑中分离出来。业务逻辑不涉及生命周期,就不会有业务代码如疏忽清理而引发的问题了。
整个上下文的传递流程或说生命周期可以规范化成:
捕捉、回放和恢复这3个操作,即CRR(capture/replay/restore)模式。
CRR :
- capture方法:抓取线程(线程A)的所有TTL值。
- replay方法:在另一个线程(线程B)中,回放在capture方法中抓取的TTL值,并返回 回放前TTL值的备份
- restore方法:恢复线程B执行replay方法之前的TTL值(即备份)
DEMO演示代码实例:
// ===========================================================================
// 线程 A
// ===========================================================================
TransmittableThreadLocal<String> context = new TransmittableThreadLocal<>();
context.set("value-set-in-parent");
// (1) 抓取当前线程的所有TTL值
final Object captured = TransmittableThreadLocal.Transmitter.capture();
// ===========================================================================
// 线程 B(异步线程)
// ===========================================================================
// (2) 在线程 B中回放在capture方法中抓取的TTL值,并返回 回放前TTL值的备份
final Object backup = TransmittableThreadLocal.Transmitter.replay(captured);
try {
// 你的业务逻辑,这里你可以获取到外面设置的TTL值
String value = context.get();
System.out.println("Hello: " + value);
...
String result = "World: " + value;
} finally {
// (3) 恢复线程 B执行replay方法之前的TTL值(即备份)
TransmittableThreadLocal.Transmitter.restore(backup);
}
2.方法
1.TransmittableThreadLocal.java
- 关键成员组件:holder 记录线程中有哪些TTL需要被传输
/**
* 1. holder 本身是一个InheritableThreadLocal
* 2. holder 的value是 WeakHashMap,map中的key是 TransmittableThreadLocal ,value 是null。相当于做set使用。
*/
private static final InheritableThreadLocal<WeakHashMap<TransmittableThreadLocal<Object>, ?>> holder =
new InheritableThreadLocal<WeakHashMap<TransmittableThreadLocal<Object>, ?>>() {
@Override
protected WeakHashMap<TransmittableThreadLocal<Object>, ?> initialValue() {
return new WeakHashMap<>();
}
@Override
protected WeakHashMap<TransmittableThreadLocal<Object>, ?>
childValue(WeakHashMap<TransmittableThreadLocal<Object>, ?> parentValue) {
return new WeakHashMap<>(parentValue);
}
};
private void addThisToHolder() {
if (!holder.get().containsKey(this)) {
holder.get().put((TransmittableThreadLocal<Object>) this, null); // WeakHashMap supports null value.
}
}
private void removeThisFromHolder() {
holder.get().remove(this);
}
- get、set、remove操作实际除了操作ThreadLocal的变量之外,还在向holder中操作TTL
public final T get() {
T value = super.get();
if (disableIgnoreNullValueSemantics || value != null) addThisToHolder();
return value;
}
public final void set(T value) {
if (!disableIgnoreNullValueSemantics && value == null) {
// may set null to remove value
remove();
} else {
super.set(value);
addThisToHolder();
}
}
public final void remove() {
removeThisFromHolder();
super.remove();
}
2.transmit
关于构词后缀er与ee的说明:
- transmit是动词传递,transmitter动作的执行者/主动方,而transmittee动作的接收者/被动方。
- er与ee后缀的常见词是employer(雇主)/employee(雇员)、caller(调用者)/callee(被调用者)。
Transmitter 用户实现 CRR 的具体操作,依赖于holder中记录的数据
具体实现为:Transmitter(传递,静态工具方法,聚合调用Transmittee实例的CRR方法) && Transmittee(被传递,每一个被传送对象的具体实现实例化,实现具体的CRR方法)
分析: ttlTransmittee 实例化实现:
private static final Transmittee<HashMap<TransmittableThreadLocal<Object>, Object>, HashMap<TransmittableThreadLocal<Object>, Object>> ttlTransmittee =
new Transmittee<HashMap<TransmittableThreadLocal<Object>, Object>, HashMap<TransmittableThreadLocal<Object>, Object>>() {
//抓取提交线程的TTL和value
@Override
public HashMap<TransmittableThreadLocal<Object>, Object> capture() {
final HashMap<TransmittableThreadLocal<Object>, Object> ttl2Value = newHashMap(holder.get().size());
for (TransmittableThreadLocal<Object> threadLocal : holder.get().keySet()) {
ttl2Value.put(threadLocal, threadLocal.copyValue());
}
return ttl2Value;
}
//重放提交线程抓取到的TTL和value
@Override
public HashMap<TransmittableThreadLocal<Object>, Object> replay(@NonNull HashMap<TransmittableThreadLocal<Object>, Object> captured) {
final HashMap<TransmittableThreadLocal<Object>, Object> backup = newHashMap(holder.get().size());
for (final Iterator<TransmittableThreadLocal<Object>> iterator = holder.get().keySet().iterator(); iterator.hasNext(); ) {
TransmittableThreadLocal<Object> threadLocal = iterator.next();
// backup
backup.put(threadLocal, threadLocal.get());
// clear the TTL values that is not in captured
// avoid the extra TTL values after replay when run task
if (!captured.containsKey(threadLocal)) {
iterator.remove();
threadLocal.superRemove();
}
}
// set TTL values to captured
setTtlValuesTo(captured);
// call beforeExecute callback
doExecuteCallback(true);
return backup;
}
//清除TTL和value
@Override
public HashMap<TransmittableThreadLocal<Object>, Object> clear() {
return replay(newHashMap(0));
}
//还原运行线程之前备份的TTL和value
@Override
public void restore(@NonNull HashMap<TransmittableThreadLocal<Object>, Object> backup) {
// call afterExecute callback
doExecuteCallback(false);
for (final Iterator<TransmittableThreadLocal<Object>> iterator = holder.get().keySet().iterator(); iterator.hasNext(); ) {
TransmittableThreadLocal<Object> threadLocal = iterator.next();
// clear the TTL values that is not in backup
// avoid the extra TTL values after restore
if (!backup.containsKey(threadLocal)) {
iterator.remove();
threadLocal.superRemove();
}
}
// restore TTL values
setTtlValuesTo(backup);
}
};
private static void setTtlValuesTo(@NonNull HashMap<TransmittableThreadLocal<Object>, Object> ttlValues) {
for (Map.Entry<TransmittableThreadLocal<Object>, Object> entry : ttlValues.entrySet()) {
TransmittableThreadLocal<Object> threadLocal = entry.getKey();
threadLocal.set(entry.getValue());
}
}
3.实现原理
如何实现从提交线程向运行线程传输TTL和value
- TransmittableThreadLocal组件 有3种使用方式:
1. 修饰Runnable和Callable
实际为Wrapper包装原始 task,在其运行前后执行CRR操作,保证task运行时的TTL上下文于提交线程相同
/**
* 包装原始task为 TTl Wrapper
*/
private TtlRunnable(@NonNull Runnable runnable, boolean releaseTtlValueReferenceAfterRun) {
//1. 抓取提交线程的TTL和value
this.capturedRef = new AtomicReference<>(capture());
this.runnable = runnable;
this.releaseTtlValueReferenceAfterRun = releaseTtlValueReferenceAfterRun;
}
/**
* wrap method {@link Runnable#run()}.
*/
@Override
public void run() {
final Object captured = capturedRef.get();
if (captured == null || releaseTtlValueReferenceAfterRun && !capturedRef.compareAndSet(captured, null)) {
throw new IllegalStateException("TTL value reference is released after run!");
}
//2. 回放提交线程的TTL和value
final Object backup = replay(captured);
try {
runnable.run();
} finally {
//3. 回放task运行线程的备份TTL和value
restore(backup);
}
}

2. 修饰线程池
TtlExecutors 工具类提供了任务提交时的task包装功能,业务调用方无需感知具体增强的task逻辑
ExecutorTtlWrapper(@NonNull Executor executor, boolean idempotent) {
this.executor = executor;
this.idempotent = idempotent;
}
@Override
public void execute(@NonNull Runnable command) {
// 重写原有任务提交方法,使用包装后的TTl 任务
executor.execute(TtlRunnable.get(command, false, idempotent));
}
3. 使用Java Agent来修饰JDK线程池实现类
Java Agent方式对应用代码无侵入
已有Java Agent中嵌入TTL Agent 代码样例
import com.alibaba.ttl.threadpool.agent.TtlAgent;
import com.alibaba.ttl.threadpool.agent.TtlTransformer;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.util.logging.Logger;
public final class YourXxxAgent {
private static final Logger logger = Logger.getLogger(YourXxxAgent.class.getName());
public static void premain(String agentArgs, Instrumentation inst) {
TtlAgent.premain(agentArgs, inst); // add TTL Transformer
// add your Transformer
...
}
}

浙公网安备 33010602011771号