当前位置:网站首页>较真儿学源码系列-InheritableThreadLocal(逐行源码带你分析作者思路)
较真儿学源码系列-InheritableThreadLocal(逐行源码带你分析作者思路)
2022-07-01 18:46:00 【天瑕】
Java版本:8u261。
之前我写过对ThreadLocal源码进行分析的文章,感兴趣的话可以查看《较真儿学源码系列-ThreadLocal(逐行源码带你分析作者思路)》。
InheritableThreadLocal是ThreadLocal的子类,通过前面的分析可知,ThreadLocal只能在同一线程内进行变量的共享,而InheritableThreadLocal不仅可以在同一线程内进行变量的共享,而且可以在父子线程之间进行变量的共享。比如说在父线程a中创建了一个子线程b,那么在a线程中用InheritableThreadLocal包装的变量,在子线程b中也能获取的到。但需要注意的是:InheritableThreadLocal和ThreadLocal一样,在同级线程中依然不能共享变量的值。并且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);
}
}以上便是InheritableThreadLocal的全部源码,可见其只是覆写了ThreadLocal的三个方法而已。而在getMap和createMap方法中可以看到inheritableThreadLocals这个属性,那么这个属性到底是干什么的呢?其实它跟threadLocals属性一样,都是放在Thread类中的属性:
public class Thread implements Runnable {
//...
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
/*
* InheritableThreadLocal values pertaining to this thread. This map is
* maintained by the InheritableThreadLocal class.
*/
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
//...
}正是因为有了inheritableThreadLocals这个属性,就可以让子线程来访问父线程中的本地变量。
在创建线程的时候,会调用到Thread类的init方法:
/**
* Thread:
*/
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
//...
Thread parent = currentThread();
//...
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
/*
将父线程中inheritableThreadLocals的数据初始化到一个新的ThreadLocalMap中,
并赋值给子线程的inheritableThreadLocals
*/
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
//...
}这里省略了其他不相干逻辑,只需要来看一下inheritableThreadLocals的初始化过程,进一步跟踪第17行代码处:
/**
* ThreadLocal:
*/
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
//parentMap是父线程中的数据
return new ThreadLocalMap(parentMap);
}
/**
* 将父线程中的inheritableThreadLocals复制到一个新的ThreadLocalMap中
* 个人认为这里直接将parentMap返回回去应该也是可以的,但这里重新构建一个
* ThreadLocalMap感觉是为了做一遍清理工作,将Entry为null的哈希槽清理掉
*/
private ThreadLocalMap(ThreadLocalMap parentMap) {
Entry[] parentTable = parentMap.table;
int len = parentTable.length;
//设置子线程的threshold
setThreshold(len);
//初始化子线程的table(因为是在子线程创建的时候调用到这里的,所以不需要判断是否已经初始化,这里一定是未初始化的)
table = new Entry[len];
//遍历父线程中的table
for (int j = 0; j < len; j++) {
//获取其中的Entry
Entry e = parentTable[j];
if (e != null) {
//获取当前槽中的Entry中保存的ThreadLocal
@SuppressWarnings("unchecked")
ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
if (key != null) {
//这里会调用InheritableThreadLocal覆写的childValue方法,也就是返回e.value本身
Object value = key.childValue(e.value);
//构建一个新的Entry
Entry c = new Entry(key, value);
//获取哈希槽的位置
int h = key.threadLocalHashCode & (len - 1);
//通过线性探测的方式来找到要插入的位置
while (table[h] != null)
h = nextIndex(h, len);
//插入数据
table[h] = c;
//计数+1
size++;
}
}
}
}
/**
* InheritableThreadLocal:
* 第32行代码处:
*/
protected T childValue(T parentValue) {
return parentValue;
}完整的流程如下:
- 首先父线程调用set或get方法时,会调用InheritableThreadLocal覆写的getMap方法返回inheritableThreadLocals,但因为其没有初始化,所以会调用覆写的createMap方法来创建ThreadLocalMap,并赋值给inheritableThreadLocals。这样父线程的inheritableThreadLocals属性就不为null了;
- 接着父线程会调用set方法往父线程的inheritableThreadLocals属性中的table数组中进行赋值;
- 然后创建子线程的时候,会调用Thread类的init方法中的ThreadLocal.createInheritedMap方法。将父线程inheritableThreadLocals属性中的数据初始化到一个新的ThreadLocalMap中,并同时赋值给子线程的inheritableThreadLocals。这样子线程的inheritableThreadLocals属性中就有了父线程中的数据;
- 最后子线程在调用get方法的时候就能拿到父线程中的数据了。但是需要注意的是:子线程执行完毕后,父线程此时调用get方法拿到的还是之前父线程中的inheritableThreadLocals,并不是子线程会往其中更改过的ThreadLocalMap。也就是说子线程的数据不会传递给父线程,子线程只有在一开始初始化的时候才会同步父线程中的数据。
原创不易,未得准许,请勿转载,翻版必究
边栏推荐
- Use the uni app demo provided by Huanxin to quickly realize one-on-one chat
- Methods of finding various limits
- js三元表达式复杂条件判断
- 使用 Kibana Timelion 进行时间序列分析
- Analysis of GetMessage underlying mechanism
- 为什么一定要从DevOps走向BizDevOps?
- pickle. Load error [attributeerror: can't get attribute 'volatile' on < module '\u main']
- ffmpeg常用命令(二)
- Intensive cultivation of channels for joint development Fuxin and Weishi Jiajie held a new product training conference
- What must be done in graduation season before going to Shanhai
猜你喜欢
![[go ~ 0 to 1] day 5 July 1 type alias, custom type, interface, package and initialization function](/img/1e/bed6a761f07c052e43b1e3b1701760.png)
[go ~ 0 to 1] day 5 July 1 type alias, custom type, interface, package and initialization function

Basic use of MySQL

如何正确使用Vertx操作Redis(3.9.4带源码分析)

Test self-study people must see: how to find test items in software testing?

解决VSCode下载慢或下载失败的问题

Regular expression =regex=regular expression

uni-app商品分类

Introduction to relevant processes and functions of wechat official account development

自定义插入页面标签以及实现类似通讯录的首字母搜索

Detailed explanation of JUnit unit test framework
随机推荐
直播HLS协议
torch. nn. functional. Interpolate function
安装sharp报错
Optaplanner learning notes (I) case cloud balance
事务隔离级别 gap锁 死锁
奔赴山海之前,毕业季一定要做的那些事情
ES6中的代理proxy
Regular expression =regex=regular expression
精耕渠道共謀發展 福昕攜手偉仕佳傑開展新產品培訓大會
Case sharing: basic networking configuration of QinQ
使用 Kibana Timelion 进行时间序列分析
A brief understanding of white box encryption technology
wireshark报文分析tcp,ftp
The key to the success of digital transformation enterprises is to create value with data
Flutter 实战-快速实现音视频通话应用
简版拼多多商品数据
Interview questions for audio and video positions in Dachang -- today's headline
MySQl的基本使用
Brpc understanding
[go ~ 0 to 1] day 5 July 1 type alias, custom type, interface, package and initialization function