【并发编程】ThreadLocal的兄弟InheritableThreadLocal
本博客系列是学习并发编程过程中的记录总结。由于文章比较多,写的时间也比较散,所以我整理了个目录贴(传送门),方便查阅。
引子
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值。
InheritableThreadLocal简单使用
还是以上面的列子为列,我们只需要将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();
}
}
执行结果如下:
value:mainThread
value:mainThread
InheritableThreadLocal原理分析
先看下InheritableThreadLocal的源代码:
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值。
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复制给子线程,后续两者就没啥关系了。
最新文章
- webix前端架构的项目应用
- https问答篇
- Structs框架
- (Java) 2014年1月1日减一个月涉及时间与字符的转换
- 用css3实现各种图标效果(2)
- js鲸鱼
- Windows Office key 持续更新地址
- ul li横向排列及圆点处理
- [Hibernate] - Load and Get
- [itint5]单词游戏
- linux环境下安装php扩展
- VS中调试时监控线程的最后错误代码
- find a lover
- C 标识符, 数据存储形式(原码,反码,补码)
- If you sleep now,you will have a dream. If you study now,you will achieve your dream.
- 每日linux命令学习-历史指令查询(history、fc、alias)
- Linux排查Java程序占用CPU很高的解决办法
- 关于css优先级
- c语言 多重排序
- 『转』谷歌发布Windows版Chrome App Launcher
热门文章
- [考试反思]0807NOIP模拟测试14:承认
- LeetCode刷题总结-数组篇(下)
- 『题解』洛谷P1063 能量项链
- [LINQ2Dapper]最完整Dapper To Linq框架(四)---Linq和SQL并行使用
- CSS3解决字母不换行的方法
- PowerMock学习之PoweMock的入门(二)
- mysql提示Packet for query is too large (1142 >; 1024)解决方案
- pat 1054 The Dominant Color(20 分)
- nyoj 255-C小加 之 随机数 (map)
- RHEL7.2 SSH无密码登录非root用户