https://blog.youkuaiyun.com/liu1pan2min3/article/details/80236105 这篇文章详细的讲解了threadLocal的原理,今天就来讲讲 InheritableThreadLocal
InheritableThreadLocal是什么东东呢,本姑娘今天就来讲解下,还是老的套路 即 是什么 怎么用 为什么,但是读懂的前提还是
在读懂ThreadLocal的源码的前提下
什么是InheritableThreadLocal呢
ThreadLocal设计之初就是为了绑定当前线程,如果希望当前线程的threadLocal能被子线程使用,实现方式就会相当困难,需要用户
自己在代码中传递,在此背景下,InheritableThreadLocal应运而生
使用场景
我来自猫眼的新业务技术部,我们有自己的日志中心服务,cat服务等等 都用到了调用链 比如交易会依赖卖品rpc服务、支付rpc服务
价格rpc服务等等 ,在日志查找的时候 根据一个traceId(调用链id)就能够把依赖的各个服务串联起来,如果业务中new 了一个子
线程或者使用了线程池 如何能把这个traceId能够在多线程之间传递呢,会用到线程池技术,当然这已经不仅仅InheritableThreadLocal
能够解决的啦,但是亲们还是先了解下InheritableThreadLocal
一个使用的小例子
public
class
InheritableThreadLocalTest {
public
static
ThreadLocal<Integer> threadLocal =
new
ThreadLocal();
public
static
void
main(String args[]){
threadLocal.set(
new
Integer(
123
));
Thread thread =
new
MyThread();
thread.start();
System.out.println(
"main = "
+ threadLocal.get());
}
static
class
MyThread
extends
Thread{
@Override
public
void
run(){
System.out.println(
"MyThread = "
+ threadLocal.get());
}
}
|
运行结果
main = 123
MyThread = null
将上面代码“public static ThreadLocal<Integer> threadLocal = new ThreadLocal();”改为“public static ThreadLocal<Integer> threadLocal = new InheritableThreadLocal();”,
运行结果:
main = 123
MyThread = 123
也就是子线程或者说新开的线程拿到了该值。
重要的事情说三遍 请先看ThreadLocal源码 请先看ThreadLocal源码 请先看ThreadLocal源码
然后来看InheritableThreadLocal源码
InheritableThreadLocal类重写了ThreadLocal的3个函数:
public
class
InheritableThreadLocal<T>
extends
ThreadLocal<T> {
/**
* 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.
* <p>
* 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);
}
}
|
点进 Thread里看 inheritableThreadLocals是啥
/*从方法已经在ThreadLocal里讲过了 是本线程的一个ThreadLocalMap */ ThreadLocal.ThreadLocalMap threadLocals = null; /* *inheritableThreadLocals 自父线程的ThreadLocalMap,主要用于父子线程ThreadLocal
变量的传递
*/ ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
Thread类中包含threadLocals 和 inheritableThreadLocals两个变量,其中inheritableThreadLocals 主要存储可自动
向子线程中传递的ThreadLocal.ThreadLocalMap。
在getMap方法里是get的线程里的inheritableThreadLocals变量
在createMap方法里是创建了inheritableThreadLocals
看InheritableThreadLocal的set方法 因为继承自ThreadLocal,看ThreadLocal里的set方法
public
void
set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if
(map !=
null
)
map.set(
this
, value);
else
createMap(t, value);
}
|
getMap方法会走子类InheritableThreadLocal里的getMap方法 去线程里的inheritableThreadLocals变量 如果为空就去创建,如果不为空就放到inheritableThreadLocals变量里
因为ThreadLocalMap的set方法已经在ThreadLocal的源码里讲了,就不继续往讲了
再看get方法
public
T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
//此map是调用的InheritableThreadLocal里的getmap方法
if
(map !=
null
) {
ThreadLocalMap.Entry e = map.getEntry(
this
);
if
(e !=
null
) {
@SuppressWarnings
(
"unchecked"
)
T result = (T)e.value;
return
result;
}
}
return
setInitialValue();
}
|
又是如何传递的呢,可能你没想到也可能想到了,是在父线程new Thread的时候 传递进去的,哇咔咔,就来看下 Thread源码
Thread t = new Thread();
点进Thread里看无参构造方法
public Thread() { init(null, null, "Thread-" + nextThreadNum(), 0); }
再看init方法
private void init(ThreadGroup g, Runnable target, String name, long stackSize) { init(g, target, name, stackSize, null); }
/**
* Initializes a Thread.
*
* @param g the Thread group
* @param target the object whose run() method gets called
* @param name the name of the new Thread
* @param stackSize the desired stack size for the new thread, or
* zero to indicate that this parameter is to be ignored.
* @param acc the AccessControlContext to inherit, or
* AccessController.getContext() if null
*/
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;
Thread parent = currentThread();
//获取创建此线程的线程,即父线程
SecurityManager security = System.getSecurityManager();
if
(g ==
null
) {
/* Determine if it's an applet or not */
/* If there is a security manager, ask the security manager
what to do. */
if
(security !=
null
) {
g = security.getThreadGroup();
}
/* If the security doesn't have a strong opinion of the matter
use the parent thread group. */
if
(g ==
null
) {
g = parent.getThreadGroup();
}
}
/* checkAccess regardless of whether or not threadgroup is
explicitly passed in. */
g.checkAccess();
/*
* Do we have the required permissions?
*/
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);
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();
}
|
重点在
if (parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
这段话
如果 父线程的inheritableThreadLocals 不为空,父线程的inheritableThreadLocals(ThreadLocalMap类型)拷贝到子线程的
的inheritableThreadLocals变量里
接着看createInheritedMap方法
static
s ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
return
new
ThreadLocalMap(parentMap);
}
private
ThreadLocalMap(ThreadLocalMap parentMap) {
Entry[] parentTable = parentMap.table;
int
len = parentTable.length;
setThreshold(len);
table =
new
Entry[len];
for
(
int
j =
0
; j < len; j++) {
Entry e = parentTable[j];
if
(e !=
null
) {
@SuppressWarnings
(
"unchecked"
)
ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
if
(key !=
null
) {
Object value = key.childValue(e.value);
Entry c =
new
Entry(key, value);
int
h = key.threadLocalHashCode & (len -
1
);
while
(table[h] !=
null
)
h = nextIndex(h, len);
table[h] = c;
size++;
}
}
}
}
|
当创建一个新的线程时,新线程就会有 ThreadLocalMap 类型的 inheritableThreadLocals ,因为它是 Thread 类的一个属性。
先得到当前线程存储的这些值,例如Entry[] parentTable = parentMap.table;
再通过一个for循环,不断的把当前
线程的这些值复制到我们新建的线程的inheritableThreadLocals中。所以在先创建的线程中调用threadLocal.get()方法,首先
会得到新线程的inheritableThreadLocals(是一个ThreadLocalMap类型) 然后再map.getEntry获取 ,我的上篇Threadlocal
源码里有讲解
作者:
敏敏,猫眼电影java开发工程师 爱学习,爱源码,更爱生活