本博客系列是学习并发编程过程当中的记录总结。因为文章比较多,写的时间也比较散,因此我整理了个目录贴(传送门),方便查阅。html
并发编程系列博客传送门java
public class InheritableThreadLocalDemo { private static ThreadLocal<String> threadLocal = new ThreadLocal<>(); public static void main(String[] args) { threadLocal.set("mainThread"); System.out.println("value:"+threadLocal.get()); Thread thread = new Thread(new Runnable() { @Override public void run() { String value = threadLocal.get(); System.out.println("value:"+value); } }); thread.start(); } }
上面代码中在主线程中设置了一个ThreadLocal变量,并将其值设置为mainThread
。而后有在主线程中开启了一个子线程thread
,并试图获取在主线程中set的ThreadLocal变量的值。可是结果以下:编程
value:mainThread value:null
经过前面的文章介绍,对于上面的结果咱们也就很是容易理解了。每一个线程都会有一个本身的ThreadLocalMap,因此子线程在调用get方法拿值的时候其实访问的是本身的ThreadLocalMap,这个Map和主线程的Map是两个不一样的对象,因此确定是拿不到值的。并发
那么Java中有没有相似的对象能实现上面的功能呢?有,InheritableThreadLocal
就能实现这样的功能,这个类能让子线程继承父线程中已经设置的ThreadLocal值。ide
仍是以上面的列子为列,咱们只须要将ThreadLocal变成InheritableThreadLocal就好了。学习
public class InheritableThreadLocalDemo { private static InheritableThreadLocal<String> threadLocal = new InheritableThreadLocal<>(); public static void main(String[] args) { threadLocal.set("mainThread"); System.out.println("value:"+threadLocal.get()); Thread thread = new Thread(new Runnable() { @Override public void run() { String value = threadLocal.get(); System.out.println("value:"+value); } }); thread.start(); } }
执行结果以下:this
value:mainThread value:mainThread
先看下InheritableThreadLocal的源代码:spa
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,而且重写了getMap和createMap方法,区别就是将 ThreadLocal 中的 threadLocals 换成了 inheritableThreadLocals,这两个变量都是ThreadLocalMap类型,而且都是Thread类的属性。线程
下面就一步步来看下InheritableThreadLocal为何能拿到父线程中的ThreadLocal值。code
step1:InheritableThreadLocal获取值先调用了get方法,因此咱们直接看看get方法都作了些啥。
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(); }
从上面的代码能够看出,get方法和ThreadLocal中是同样的,惟一有区别的就是其中的getMap方法重写了,返回的是inheritableThreadLocals属性。这个属性也是一个ThreadLocalMap类型的变量。那么从这边就能够推断出来:确定是在某处将父线程中的ThreadLocal值赋值到了子线程的inheritableThreadLocals中。
step2:在源代码中搜索哪些地方使用到了inheritableThreadLocals
这个属性,最后找到这段代码:
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); //1. 这边先判断了父线程中inheritableThreadLocals属性是否为空,不为空的话就复制给子线程 if (parent.inheritableThreadLocals != null) this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals); /* Stash the specified stack size in case the VM cares */ this.stackSize = stackSize; /* Set thread ID */ tid = nextThreadID(); }
上面的代码印证了咱们的猜测。须要注意的是一旦子线程被建立之后,再操做父线程中的ThreadLocal变量,那么子线程是不能感知的。由于父线程和子线程仍是拥有各自的ThreadLocalMap,只是在建立子线程的“一刹那”将父线程的ThreadLocalMap复制给子线程,后续二者就没啥关系了。
原文出处:https://www.cnblogs.com/54chensongxia/p/12015443.html