https://blog.csdn.net/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 managerwhat to do. */ if (security != null) {
g = security.getThreadGroup();}/* If the security doesn't have a strong opinion of the matteruse the parent thread group. */ if (g == null) {
g = parent.getThreadGroup();}}/* checkAccess regardless of whether or not threadgroup isexplicitly 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();
elsethis.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开发工程师 爱学习,爱源码,更爱生活