InheritableThreadLocal

本文详细解析InheritableThreadLocal的工作原理及应用场景,通过对比ThreadLocal,介绍如何实现跨线程传递变量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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开发工程师 爱学习,爱源码,更爱生活


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值