ThreadLocal,InheritableThreadLocal,TTL全解
一、ThreadLoal 详解
ThreadLocal 是保存线程的使用变量。
ThreadLocal具体方法如下:
public class ThreadLocal<T> {
/**
* 1.8以后提供的,返回一个有初始值的ThreadLocal
*/
public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {
return new SuppliedThreadLocal<>(supplier);
}
/**
* 获取当前线程保存的变量
*/
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();
}
/**
* 设置当前线程的值
*/
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
/**
* 清除当前线程的值:当前线程退出时,最后要调用一下清除方法。
*/
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
/**
* 创建一个ThreadLocalMap,多个线程保存变量个关键的关键
*/
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
}
通过以代码可以看到ThreadLocalMap是每个线程持有的。所以保证线程隔离。ThreadLocalMap中key使用的使用的弱引用,在相同GC时,就会被清理掉。threadLocal生命周期里会尽可能的保证不出现内存泄漏的问题,达到安全的状态。
那么ThreadLocal会有内存泄露的问题吗?答案是会的。避免内存泄露的方法,是在使用完ThreadLocal后,要调用remove方法,将其清除掉。
二、InheritableThreadLocal 详解
InheritableThreadLocal 用于子线程创建时,自动继承父线程的ThreadLocal变量。
可以看出InheritableThreadLocal 是继承ThreadLocal并重写了其中的3个方法。实现子线程获取父线程的变量,是在Thread的初始化的时候。
默认是TRUE,也就是
初始化的时候,将父线程的所以变量都遍历复制一遍,这也子线程就自动的获取到父线程的值了。
注意:InheritableThreadLocal在线程池使用的时候,就会失效。如果想用线程池,可以考虑使用阿里的TTL
三、阿里TTL的使用
阿里的transmittable-thread-local简称TTL,TTL继承并加强InheritableThreadLocal类。
典型使用场景
分布式跟踪系统
应用容器或上层框架跨应用代码给下层SDK传递信息
日志收集记录系统上下文
使用类TransmittableThreadLocal来保存上下文,并跨线程池传递。TransmittableThreadLocal继承InheritableThreadLocal,使用方式也类似。比InheritableThreadLocal,添加了protected方法copy,用于定制 任务提交给线程池时的上下文传递到 任务执行时时的拷贝行为,缺省是传递的是引用。
可以说TTL是InheritableThreadLocal的加强版。使用方法:
- 使用com.alibaba.ttl.TtlRunnable和com.alibaba.ttl.TtlCallable来修饰传入线程池的Runnable和Callable。
- 省去每次Runnable和Callable传入线程池时的修饰,这个逻辑可以在线程池中完成。
通过工具类com.alibaba.ttl.threadpool.TtlExecutors完成,有下面的方法:
getTtlExecutor:修饰接口Executor
getTtlExecutorService:修饰接口ExecutorService
ScheduledExecutorService:修饰接口ScheduledExecutorService - 使用Java Agent来修饰JDK线程池实现类
总结
通过以上分析,结合不同的场景,应用不同的ThreadLocal,达到自己的目的。