一、InheritableThreadLocal 是什么?
InheritableThreadLocal 是 Java 中的一个类,它是 ThreadLocal 类的一个子类。与 ThreadLocal 不同的是,InheritableThreadLocal 可以在父线程和子线程之间共享数据,子线程可以继承父线程的 InheritableThreadLocal 变量的值。这意味着,当一个线程创建了一个子线程时,子线程可以访问父线程中的 InheritableThreadLocal 变量的值,而不是创建一个新的变量。这种机制可以使得在多个线程之间共享数据变得更加方便。
注意:InheritableThreadLocal 本身并不提供线程安全的保证,因为它只是提供了一种在多线程之间共享数据的机制,而不涉及数据的同步。因此,在使用 InheritableThreadLocal 时,需要自己保证线程安全。可以通过以下方法保证线程安全:
将InheritableThreadLocal 对象的访问范围限制在单个线程中,避免多个线程同时访问同一个对象。
对于需要跨线程共享的变量,使用线程安全的方式来访问。可以使用synchronized 关键字或者使用java.util.concurrent包中的线程安全类。
另外,需要注意的是,当 InheritableThreadLocal 变量被使用时,需要及时地清理它的值,避免出现内存泄漏的情况。
二、InheritableThreadLocal 使用场景
InheritableThreadLocal 主要用于需要在线程之间共享数据,并且希望子线程能够继承父线程的数据的场景。
在 Web 应用程序中,将用户信息存储在 InheritableThreadLocal 变量中,这样在处理用户请求的过程中,不同的线程都可以访问到用户信息,而且在子线程中也可以继承父线程的用户信息,从而避免了在不同的线程中反复传递用户信息的麻烦。
InheritableThreadLocal 也可以用于在框架中传递上下文信息,比如在分布式系统中,将请求的 trace id 存储在 InheritableThreadLocal 中,从而在整个请求链路中都能够方便地获取到 trace id。
总之,InheritableThreadLocal 可以方便地在父子线程之间传递数据,但是需要注意线程安全问题。如果多个线程同时访问同一个 InheritableThreadLocal 变量,需要保证线程安全。
三、源码分析
public class InheritableThreadLocal<T> extends ThreadLocal<T> {
// 是一个回调方法,在子线程中初始化该线程本地变量的值。该方法的参数是父线程中该变量的值,返回值是父线程中该变量的值。
protected T childValue(T parentValue) {
return parentValue;
}
// 返回当前线程的 inheritableThreadLocals 属性,该属性是一个 ThreadLocalMap 对象,用于存储 InheritableThreadLocal 类的线程本地变量
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
// 创建一个新的 ThreadLocalMap 对象,并将其赋值给当前线程的 inheritableThreadLocals 属性
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
}
InheritableThreadLocal类继承自 ThreadLocal 类,拥有 ThreadLocal 的特性,每个线程都有一个独立的变量副本。不同之处在于,InheritableThreadLocal 变量可以被子线程继承。这个类包含了三个方法:
childValue(T parentValue) 方法:该方法是一个回调方法,用于在子线程中创建该变量的值。默认情况下,子线程会继承父线程的 InheritableThreadLocal 变量副本,但是如果需要在子线程中创建一个新值,可以通过重写这个方法来实现。这个默认实现是简单地返回父线程的值,意味着子线程会与父线程共享同一个值。如果需要在子线程中创建一个新值,可以通过重写这个方法来提供自己的逻辑。
getMap(Thread t) 方法:该方法返回指定线程的 InheritableThreadLocalMap 对象,这个对象包含了该线程的所有 InheritableThreadLocal 变量。如果该线程的InheritableThreadLocal属性为null,则会返回null。
createMap(Thread t, T firstValue) 方法:这个方法用于在指定线程中创建 InheritableThreadLocalMap 对象,并将第一个变量值放入其中。如果该线程已经有 InheritableThreadLocalMap 对象,则不会创建新的对象。
四、InheritableThreadLocal 设置值和获取值流程
4.1 set 流程
首先创建一个 InheritableThreadLocal 实例,例如:InheritableThreadLocal<String> local = new InheritableThreadLocal<>();
调用 local .set(value) 方法设置值(实际上调用的是InheritableThreadLocal父类中的set方案进行设置值)。
获取当前线程;
根据当前线程从InheritableThreadLocal 中获取对应的ThreadLocalMap对象。
如果ThreadLocalMap对象存在,则使用set方法将ThreadLocal对象和值保存到ThreadLocalMap对象中。
如果ThreadLocalMap对象不存在,说明当前线程还没有ThreadLocalMap对象,此时调用InheritableThreadLocal的createMap方法为其创建一个ThreadLocalMap对象,将ThreadLocal对象和值保存到其中,并将ThreadLocal赋值给当前线程inheritableThreadLocals属性。
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
4.2 get 流程
获取当前线程;
根据当前线程从InheritableThreadLocal中获取对应的ThreadLocalMap对象。
如果ThreadLocalMap对象存在,则从中获取ThreadLocal对象对应的Entry对象,
如果Entry对象存在,则返回其值,否则返回null。
如果ThreadLocalMap对象不存在,说明当前线程还没有ThreadLocalMap对象,此时需要使用setInitialValue方法来为其设置一个初始值,并返回该值。
// 如果没有获取到数据,则会初始一个数据,将线程当作key,null 当作value 放入ThreadLocalMap中
public T get() {
// 获取当前线程。
Thread t = Thread.currentThread();
// 根据当前线程从InheritableThreadLocal中获取ThreadLocalMap对象
ThreadLocalMap map = getMap(t);
// 如果ThreadLocalMap对象存在
if (map != null) {
// 从中获取ThreadLocal对象对应的Entry对象。
ThreadLocalMap.Entry e = map.getEntry(this);
// 如果Entry对象存在,则返回其值
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
// 如果ThreadLocalMap对象不存在,说明当前线程还没有ThreadLocalMap对象,此时需要使用setInitialValue方法来为其设置一个初始值,并返回该值。
return setInitialValue();
}