当前位置: 代码迷 >> 综合 >> InheritableThreadLocal (ThreadLocal的升级,用于将父线程的本地变量传给子线程)
  详细解决方案

InheritableThreadLocal (ThreadLocal的升级,用于将父线程的本地变量传给子线程)

热度:72   发布时间:2023-12-27 13:25:16.0

1.子线程如何通过 InheritableThreadLocal 获取父线程的 可继承 线程变量的?

 实现原理:

 

相关类:

/*****/package java.lang;
import java.lang.ref.*;** @author  Josh Bloch and Doug Lea* @see     ThreadLocal* @since   1.2*/public class InheritableThreadLocal<T> extends ThreadLocal<T> {protected T childValue(T parentValue) {return parentValue;}/*** Get the map associated with a ThreadLocal.*  InheritableThreadLocal  的 set() 方法会 用到 ,来判断  子线程的 inheritableThreadLocals (即 map)  是否初始化* @param t the current thread*/ThreadLocalMap getMap(Thread t) {return t.inheritableThreadLocals;}/*** Create the map associated with a ThreadLocal.*nheritableThreadLocal  的 set() 方法会 用到 ,来判断  子线程的 inheritableThreadLocals (即 map)  是否初始化 ,没有初始化 ,调用 此方法进行初始化 * @param t the current thread* @param firstValue value for the initial entry of the table.*/void createMap(Thread t, T firstValue) {// 给 子线程 设置  inheritableThreadLocals 属性:在Thread 类里是这样定义的//  ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);}
}

 

 

 

1.

ThreadLocal 设计初衷是为了在多线程环境下,针对每一个线程能有一个自己的副本,这样可以在一定程度上解决多线程并发修改的问题。但是,我们可以在此基础上做一个拓展,比如context,我们可以利用 ThreadLocal 针对每一个线程都有一个自己的上下文,一般都是写成ThreadLocal<Context>,这样在这个线程上做的所有修改都可以被大家利用到。

先用 ThreadLocal

package com.xxl.job.executor.service.jobhandler;/*** @program: * @description:* @author: gyg* @create: 2021-08-01 12:46**/
public class ThreadLocalTest {private static ThreadLocal<Context> context = new ThreadLocal<>();static class Context {String name;int value;}public static void main(String[] args) {Context context = new Context();context.name = "mainName";context.value = 10;// 给父线程 设置 本地变量ThreadLocalTest.context.set(context);// 创建子线程Thread childThread = new Thread(new Runnable() {@Overridepublic void run() {Context childContext = ThreadLocalTest.context.get();System.out.println(childContext.name);System.out.println(childContext.value);}});// 启动子线程childThread.start();}
}

 

运行 main 方法之后,直接在子线程中抛错,这样确实符合我们的预期,但如果我们想达到子线程可以获取到父线程的 context这样的效果该如何做呢?首先想到的就是在生成子线程的时候,将父线程 ThreadLocal 里的值传给子线程。这样做虽然能达到效果,但过程比较繁杂,且代码侵入性强。这个时候就可以用InheritableThreadLocal了。
package com.xxl.job.executor.service.jobhandler;/*** @program:  可继承父类变量的 ThreadLocal------>InheritableThreadLocal* @description:* @author: gyg* @create: 2021-08-01 12:46**/
public class InheritableThreadLocalTest {private static InheritableThreadLocal<Context> context = new InheritableThreadLocal<>();static class Context {String name;int value;}public static void main(String[] args) {Context context = new Context();context.name = "mainName";context.value = 10;// 给父线程 设置 本地变量InheritableThreadLocalTest.context.set(context);// 创建子线程Thread childThread = new Thread(new Runnable() {@Overridepublic void run() {Context childContext = InheritableThreadLocalTest.context.get();System.out.println(childContext.name);System.out.println(childContext.value);}});// 启动子线程childThread.start();}
}

运行结果:

 改造:

package com.xxl.job.executor.service.jobhandler;import sun.rmi.server.InactiveGroupException;/*** @program:  可继承父类变量的 ThreadLocal------>InheritableThreadLocal* @description:* @author: gyg* @create: 2021-08-01 12:46**/
public class InheritableThreadLocalTest {private static InheritableThreadLocal<Context> contextThreadLocal = new InheritableThreadLocal<>();private static InheritableThreadLocal<Integer> integerThreadLocal = new InheritableThreadLocal<>();static class Context {String name;int value;}public static void main(String[] args) {Context context = new Context();context.name = "mainName";context.value = 10;// 给父线程 设置 本地变量InheritableThreadLocalTest.contextThreadLocal.set(context);InheritableThreadLocalTest.integerThreadLocal.set(10000);// 创建子线程Thread childThread = new Thread(new Runnable() {@Overridepublic void run() {Context childContext = InheritableThreadLocalTest.contextThreadLocal.get();System.out.println(childContext.name);System.out.println(childContext.value);Integer sonIntegerValue = InheritableThreadLocalTest.integerThreadLocal.get();System.out.println(sonIntegerValue);}});// 启动子线程childThread.start();}
}

说明子线程 拿到了  父线程的 本地变量: 

源码实现:

① 给子线程 设置 父线程的 线程变量

new Thread 的时候 底层 会走如下方法:

 // 此方法 在 Thead 类里 ,是 在  new Thread 的时候 调用的 private void init(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc,boolean inheritThreadLocals) {// 省略无关代码......//  获取当前线程(将来的父线程)Thread parent = currentThread();// 省略无关代码......// 将父线程的 值 赋给 子线程 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 (inheritThreadLocals && parent.inheritableThreadLocals != null)//  设置 子线程 的  inheritableThreadLocals  属性 (是一个map)this.inheritableThreadLocals =ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);/* Stash the specified stack size in case the VM cares */this.stackSize = stackSize;/* Set thread ID */tid = nextThreadID();}

看一下:  线程 的  inheritableThreadLocals  属性 

 /* ThreadLocal values pertaining to this thread. This map is maintained* by the ThreadLocal class. */ThreadLocal.ThreadLocalMap threadLocals = null;/** InheritableThreadLocal values pertaining to this thread. This map is* maintained by the InheritableThreadLocal class.*/ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

如何 创建子线程的 inheritableThreadLocals ?

   
// parentMap 是父线程的 inheritableThreadLocals 
private ThreadLocalMap(ThreadLocalMap parentMap) {// 获取父线程 的  mapEntry[] parentTable = parentMap.table;int len = parentTable.length;setThreshold(len);//  创建子线程  的  Map 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 : key  是 ThreadLocal 引用 Entry c = new Entry(key, value);int h = key.threadLocalHashCode & (len - 1);//  while 循环  获取子线程 的空槽位 while (table[h] != null)h = nextIndex(h, len);// 将 刚 构建 子线程  的 Entry  放到 数组 里table[h] = c;size++;}}}}

TheadLocal  里 Entry的 定义:

static class Entry extends WeakReference<ThreadLocal<?>> {/** The value associated with this ThreadLocal. */Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value = v;}}

断点测试:

 ② 子线程 读取 从父线程 得来的  线程变量

package com.xxl.job.executor.service.jobhandler;import sun.rmi.server.InactiveGroupException;/*** @program:  可继承父类变量的 ThreadLocal------>InheritableThreadLocal* @description:* @author: gyg* @create: 2021-08-01 12:46**/
public class InheritableThreadLocalTest {private static InheritableThreadLocal<Integer> integerThreadLocal = new InheritableThreadLocal<>();public static void main(String[] args) {// 给父线程 设置 本地变量//InheritableThreadLocalTest.integerThreadLocal.set(10000);// 创建子线程Thread childThread = new Thread(new Runnable() {@Overridepublic void run() {// 获取 从父线程 得到的值 ,get() 方法 没有重写,还是用父类(ThreadLocal的 )Integer sonIntegerValue = InheritableThreadLocalTest.integerThreadLocal.get();System.out.println(sonIntegerValue);}});// 启动子线程childThread.start();}
}
 public T get() {Thread t = Thread.currentThread();// 这儿注意 :  InheritableThreadLocal  重写了ThreadLoca 的 getMap 方法,所以// 此处 getMap走的是 子类重写后的方法//  重写后的方法如下://       ThreadLocalMap getMap(Thread t) {//           return t.inheritableThreadLocals;//        }   //    也就是 说:// 从 Thread 对象的 // ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;  这个属性里获取值// 上面的源码已经分析过,在 new  子线程的时候,会将 父线程的 // inheritableThreadLocals  里的值 拷贝给子线程   ThreadLocalMap map = getMap(t);if (map != null) {ThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {@SuppressWarnings("unchecked")T result = (T)e.value;return result;}}return setInitialValue();}

 

 

  相关解决方案