经过 ThreadLocal实现原理 这一篇的分析。我们了解了ThreadLocal的原理,也知道我们可以使用ThreadLocal来保存线程的局部变量,但是我们也知道,一个线程保存的值,另外一个线程也是访问不了的,那么问题也随之而来了,比如我们有时候想要在子线程中访问父线程中ThreadLoca中的值,那么该怎么办呢?
InheritableThreadLocal
使用InheritableThreadLocal就能做到,这个类是ThreadLocal的子类,我们来看下它的实现
public class InheritableThreadLocal<T> extends ThreadLocal<T> {
protected T childValue(T parentValue) {
return parentValue;
}
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
}
继承了ThreadLocal,并且重写了其中的三个方法
我们先来看下怎么使用InheritableThreadLocal,以便于我们更好的介绍这个类的实现
public void testInheritableThreadLocal() throws InterruptedException {
InheritableThreadLocal<String> threadLocal = new InheritableThreadLocal<>();
threadLocal.set("MainThread");
Thread thread = new Thread(() -> System.out.println("子线程取出的值" + threadLocal.get()));
thread.start();
System.out.println("父线程取出的值" + threadLocal.get());
thread.join();
}
执行结果
我们先从这行代码开始介绍
threadLocal.set("MainThread");
具体实现
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
这里在我之前那篇ThreadLocal原理中就分析过这个方法。这里拿出来说是要你们注意的是,getMap(t)和createMap(t, value) 这两个方法是被InheritableThreadLocal重写来的,所以它调用的应该是这两个实现
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
同样,inheritableThreadLocals这个属性也被定义在Thread中
当我们第一次调用set(t)这个方法的时候会初始化这个变量具体看void createMap(Thread t, T firstValue)这个方法,上面有把这个实现贴出来
接着我们来看这一行代码
Thread thread = new Thread(
() -> System.out.println("子线程取出的值" + threadLocal.get()));
这一行就是创建了一个Thread,并且去InheritableThreadLocal中去取值,那么它是怎么取到值的呢?
我们先来看 new Thread() 到底发生了什么,跟着Thread的构造函数进来,找到这个构造函数
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name.toCharArray();
// 把当前创建这个线程的线程的作为父线程
Thread parent = currentThread();
SecurityManager security = System.getSecurityManager();
if (g == null) {
if (security != null) {
g = security.getThreadGroup();
}
if (g == null) {
g = parent.getThreadGroup();
}
}
g.checkAccess();
if (security != null) {
if (isCCLOverridden(getClass())) {
security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
}
g.addUnstarted();
this.group = g;
// 这里稍微提一下,如果当前线程的父线程是守护线程的话那么这个子线程也将是守护进程
this.daemon = parent.isDaemon();
this.priority = parent.getPriority();
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext =
acc != null ? acc : AccessController.getContext();
this.target = target;
setPriority(priority);
// 如果父线程中的inheritableThreadLocal不为null
// 则创建一个新的 ThreadLocalMap对象,并且把父线程中 inheritableThreadLocal
// 的值复制到 当前线程的 inheritableThreadLocal中
if (parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
this.stackSize = stackSize;
tid = nextThreadID();
}
为了你们能够方便找到这段代码,以及在这里能够方便查看,我把原来的英文注释给去掉了,只留下了我写的注释
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);的实现
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
// 创建一个新的ThreadLocalMap对象并把父线程中ThreadLocalMap中的值复制到这个对象中
return new ThreadLocalMap(parentMap);
}
然后接下来我们来查看这一行代码中的 threadLocal.get()方法。看它是怎么拿到值的
相信大部分人到这里应该明白了是怎么拿到值的。不明白的也没有关系。我们接着看
Thread thread = new Thread(
() -> System.out.println("子线程取出的值" + threadLocal.get()));
同样这段代码我们也在ThreadLocal的原理中分析过,仍然值得我们注意的是 getMap(t)是调用的InheritableThreadLocal类的
public T get() {
Thread t = Thread.currentThread();
//这里调用的是InheritableThreadLocal类的getMap(t)
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();
}
InheritableThreadLocal类中的getMap(t)方法就是返回当前线程中inheritableThreadLocals这个属性,而当我们new Thread()的时候就会把父线程中的inheritableThreadLocals属性中的值拿出来并复制到一个新的ThreadLocalMap对象中
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
总结
对于上面这个例子,当我们创建InheritableThreadLocal对象,并且调用了set(t)方法后,InheritableThreadLocal就会帮我们把当前线程所对应的Thread类中的inheritableThreadLocals这个属性初始化并进行赋值,而当我们new Thread() 对象时 Thread的构造函数会判断父线程的inheritableThreadLocals有没有初始化,如果初始化了 就会把父线程中的inheritableThreadLocals中的值复制到一个新创建的 ThreadLocalMap中然后赋值给子线程中的inheritableThreadLocals属性,那么子线程中的inheritableThreadLocals就有了父线程中inheritableThreadLocals的值了
注意
对于
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
return new ThreadLocalMap(parentMap);
}
这个方法我在上面一直说的是复制父ThreadLocalMap的值然后创建一个新的ThreadLocalMap对象然后这个新创建的ThreadLocalMap就包含了父ThreadLocalMap的所有值
我这里说复制只是为了方便你们理解,至于底层实现到底是不是复制的你们感兴趣的研究下
肯定有讲错了,或者讲的不好的地方,欢迎各路大神指正