一. 问题记录
线上使用ThreadLocal临时存储用户信息传递到其它下游服务,但是偶尔会出现下游获取的用户信息不是当前用户的情况,经过详细排查,发现了是ThreadLocal导致的问题。
二. 问题原因
ThreadLocal在异步场景下是无法给子线程共享父线程中创建的线程副本数据的,也就是说当前线程的子线程无法获取到当前线程ThreadLocal中的值。
三. 解决方案
1. InheritableThreadLocal(ITL)
InheritableThreadLocal是JDK的一个类
public class InheritableThreadLocalDemo {
public static void main(String[] args) {
ThreadLocal<String> ThreadLocal = new ThreadLocal<>();
ThreadLocal<String> inheritableThreadLocal = new InheritableThreadLocal<>();
ThreadLocal.set("父类数据:threadLocal");
inheritableThreadLocal.set("父类数据:inheritableThreadLocal");
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("子线程获取父类ThreadLocal数据:" + ThreadLocal.get());
System.out.println("子线程获取父类inheritableThreadLocal数据:" + inheritableThreadLocal.get());
}
}).start();
}
}
打印结果:
子线程获取父类ThreadLocal数据:null
子线程获取父类inheritableThreadLocal数据:父类数据:inheritableThreadLocal
实现原理是子线程是通过在父线程中通过调用new Thread()方法来创建子线程,Thread#init方法在Thread的构造方法中被调用。在init方法中拷贝父线程数据到子线程中:
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
this.stackSize = stackSize;
tid = nextThreadID();
}
但InheritableThreadLocal仍然有缺陷,一般我们都是使用的线程池,而InheritableThreadLocal是在new Thread中的init()方法给赋值的,而线程池是线程复用的逻辑,所以这里还是会存在问题。
2. TransmittableThreadLocal(TTL)
TransmittableThreadLocal(TTL)是阿里巴巴开源的组件,可以解决线程池下ThreadLocal值传递问题。
四. 总结
单线程:ThreadLocal
Thread创建的线程:InheritableThreadLocal
线程池创建的线程:TransmittableThreadLocal(推荐)
247

被折叠的 条评论
为什么被折叠?



