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

InheritableThreadLocal

相关文章:

  • 2021-10-31
  • 2021-07-09
  • 2022-12-23
  • 2022-12-23
  • 2021-08-31
  • 2021-05-22
  • 2021-04-24
  • 2021-07-13
猜你喜欢
  • 2021-08-21
  • 2022-01-07
  • 2021-09-30
相关资源
相似解决方案