免费SSL申请并使用Yarp实现支持内网穿透
仗谱谒督在前文中我们讲过ThreadLocal,相当于是每个线程有一个小书包,线程之间的小书包是隔离的,只存放了属于当前线程自己的变量,因此不会发生数据安全的问题。
(前文博客浅谈ThreadLocal----每个线程一个小书包 https://www.cnblogs.com/jilodream/p/19118986)
但是有时任务太繁重时,父线程希望new出新的子线程来为自己的业务提供帮助,同时希望子线程在处理时,也能用到自己保存在ThreadLocal中的变量。
现在有三种办法:
(1)直接把父线程的ThreadLocalMap传递给子线程,让子线程直接拿去用
如:
sonThread.threadLocals=parent.threadLocals
(2)父线程在创建子线程时将子线程需要用到的ThreadLocal数据,专门指定。
如:
复制代码
Thread sonThread=new Thread(new Runnable() {
@Override
public void run() {
threadLocal1.set(xxx1); //手动指定
threadLocal2.set(xxx2); //手动指定
threadLocal3.set(xxx3); //手动指定
}
});
复制代码
(3)将父线程中threadLocals 中的数据,打包整理后,统一传递给子线程。
第一种显然不行,如果两者指向了相同的Map 会导致缓存数据被共享,父子线程存在并发读写,又会导致安全问题。
方法二虽然灵活,但是指定起来过于繁琐,每个子线程都要单独设置一遍。
java选用的是方法三,但是实现起来稍有不同:
java 定义了一个InheritableThreadLocal类。通过这个类来实现线程可继承的ThreadLocal
定义中的:
Inheritable [?n'her?t?bl]
adj. 可继承的,会遗传的
InheritableThreadLocal指可以继承/遗传的ThreadLocal。接下来看源码:
在Thread类中,定义了以下属性:
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
类似于thread.threadLocals,这是给InheritableThreadLocal 来存储可以继承的属性的Map。
InheritableThreadLocal类的源码,它继承了ThreadLocal:
复制代码
public class InheritableThreadLocal extends ThreadLocal {
/**
* Creates an inheritable thread local variable.
*/
public InheritableThreadLocal() {}
/**
* Computes the child's initial value for this inheritable thread-local
* variable as a function of the parent's value at the time the child
* thread is created. This method is called from within the parent
* thread before the child is started.
*
* This method merely returns its input argument, and should be overridden
* if a different behavior is desired.
*
* @param parentValue the parent thread's value
* @return the child thread's initial value
*/
protected T childValue(T parentValue) {
return parentValue;
}
/**
* Get the map associated with a ThreadLocal.
*
* @param t the current thread
*/
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
/**
* Create the map associated with a ThreadLocal.
*
* @param t the current thread
* @param firstValue value for the initial entry of the table.
*/
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
}
复制代码
通过源码我们可以发现,它的用法和实现与ThreadLocal基本相同,但是它重写了父类(ThreadLocal)的几个方法:
(1)
首先是getMap(),重写的目的是当需要操作map对象时,(防盗连接:本文首发自http://www.cnblogs.com/jilodream/ )请使用inheritableThreadLocals而不是再是父类中的t.threadLocals写法,这样所有使用的map就都切换到了
ThreadLocal.ThreadLocalMap inheritableThreadLocals这个属性上来了。
(2)
其次是createMap() 方法,该方法的主要目的是初始化Map对象
这里强调了初始化的是 t.inheritableThreadLocals 属性。为啥没直接用getMap() 方法来获取 t.inheritableThreadLocals呢?这是由于 t.inheritableThreadLocals 还没有初始化,返回是null,你调用getMap()没有意义。
(3)
最后是 childValue() 方法,它是指当发生继承动作时,父类中的存储的变量转化为子类对象的转化转换。这里直接抽象成方法了,方便大家重写自己的InheritableThreadLocal类时,可以直接重写该方法。
如我们想前边加一个标签,代表是子线程专用,亦或者进行翻译转换等等,总之方便你自己实现转换。
来看下继承动作具体是怎么操作的:
继承主要发生在主线程创建子线程程时,我们来看下Thread的构造方法源码,经过一堆跳转最后会跳转进这个方法:
复制代码
private Thread(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
//....
if (inheritThreadLocals && 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 */
this.tid = nextThreadID();
}
复制代码
方法参数中的boolean inheritThreadLocals表示是否要继承/遗传。手动创建的线程,这里都会是true,代表要继承/遗传。
parent.inheritableThreadLocals != null 判断父线程的inheritableThreadLocals 属性是否为null (是否初始化了),如果不为null(父线程有需要遗传的本地变量),才发生遗传动作。
条件都满足则发生遗传,(防盗连接:本文首发自http://www.cnblogs.com/jilodream/ )入参为父线程的遗传本地变量,也就是属性inheritableThreadLocals。
之后进入ThreadLocalMap的构造方法,开始构造子线程的inheritableThreadLocals 属性:
复制代码
private ThreadLocalMap(ThreadLocalMap parentMap) {
Entry[] parentTable = parentMap.table;
int len = parentTable.length;
setThreshold(len);
table = new Entry[len];
for (Entry e : parentTable) {
if (e != null) {
@SuppressWarnings("unchecked")
ThreadLocal
