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() {
@Override
public 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() {
@Override
public 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() {
@Override
public 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();
else
this.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) {
// 获取父线程 的 map
Entry[] 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() {
@Override
public 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();
}