相关文章:
InheritableThreadLocal 继承自 ThreadLocal,用于提供从父线程到子线程值的继承
一、方法解析
1、childValue(T parentValue) 相关方法
-
childValue(T parentValue)
protected T childValue(T parentValue) { return parentValue; }
-
在创建子线程时,根据父线程的值来计算可被继承的局部变量在子线程中的初始值
-
在启动子线程之前,会在父线程中调用此方法,此方法仅返回其输入参数,如果需要其他行为,则需要重写此方法
-
2、getMap(Thread t) 相关方法
-
getMap(Thread t)
ThreadLocalMap getMap(Thread t) { return t.inheritableThreadLocals; }
- 获取与 InheritableThreadLocal 相关联的 ThreadLocalMap
3、createMap(Thread t, T firstValue) 相关方法
-
createMap(Thread t, T firstValue)
void createMap(Thread t, T firstValue) { t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue); }
- 创建与 InheritableThreadLocal 相关联的 ThreadLocalMap
二、流程分析
-
通过第一步我们会发现,InheritableThreadLocal 仅仅是继承了 ThreadLocal,并重写了 ThreadLocal 中的三个方法而已,那么 InheritableThreadLocal 到底有什么用呢?让我们一起从线程的创建开始说起
-
1、创建线程
Thread thread = new Thread();
-
2、Thread 构造方法
public Thread() { init(null, null, "Thread-" + nextThreadNum(), 0); }
private void init(ThreadGroup g, Runnable target, String name, long stackSize) { init(g, target, name, stackSize, null, true); }
private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc, boolean inheritThreadLocals) { // ...... if (inheritThreadLocals && parent.inheritableThreadLocals != null) this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals); // ...... }
-
如上所示,Thread 在创建时会调用最基层的构造方法
-
该构造方法中,有个流程判断
inheritThreadLocals && parent.inheritableThreadLocals != null
,如果结果为 true,会将父线程inheritableThreadLocals
中属性的值继承到自己的inheritableThreadLocals
中-
inheritThreadLocals
- 该参数为 true 时,表示从子线程会从父线程中继承相关变量
-
parent.inheritableThreadLocals != null
- 只有父线程的
inheritableThreadLocals
不为 null,子线程才可以从父线程inheritableThreadLocals
中继承相关属性值
- 只有父线程的
-
-
-
3、创建 ThreadLocalMap
-
当满足第 2 步中的流程判断时,会调用 ThreadLocal 的
createInheritedMap(ThreadLocalMap parentMap)
方法来创建 ThreadLocalMapstatic ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) { return new ThreadLocalMap(parentMap); }
-
createInheritedMap(ThreadLocalMap parentMap)
方法中又调用了 ThreadLocalMap 的构造方法ThreadLocalMap(ThreadLocalMap parentMap)
来创建 ThreadLocalMapprivate 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 对象 Entry e = parentTable[j]; // 如果 Entry 对象不为 null if (e != null) { @SuppressWarnings("unchecked") /* * 获取弱引用关联的 ThreadLocal 对象 * (我们可以简单地将其视为 ThreadLocalMap 的 key,实际上是其弱引用) */ ThreadLocal<Object> key = (ThreadLocal<Object>) e.get(); // 如果 key 不为 null if (key != null) { /* * 调用 InheritableThreadLocal 的 * childValue(T parentValue) 方法来获取 value */ Object value = key.childValue(e.value); Entry c = new Entry(key, value); // 计算 key 对应的哈希桶数组下标 int h = key.threadLocalHashCode & (len - 1); // 如果哈希桶数组对应下标处已经有元素存在,则计算下一个下标 while (table[h] != null) h = nextIndex(h, len); // 将 Entry 对象存放至哈希桶数组新计算出来的下标处 table[h] = c; size++; } } } }
-
在 ThreadLocalMap 的构造方法
ThreadLocalMap(ThreadLocalMap parentMap)
中,由于只有创建 InheritableThreadLocal 时,才会调用此方法,因此此处的key
为 InheritableThreadLocal 对象,调用的是 InheritableThreadLocal 中重写的childValue(T parentValue)
方法protected T childValue(T parentValue) { return parentValue; }
-
-
-
通过上面三个步骤,我们可以知道,每个线程在创建时,都会在内部创建一个 ThreadLocal.ThreadLocalMap 类型的变量
inheritableThreadLocals
,子线程会从父线程的inheritableThreadLocals
中继承相关变量 -
那么再让我们来看看存储和读取的流程
-
1、创建 InheritableThreadLocal
ThreadLocal<Integer> threadLocal = new InheritableThreadLocal<>()
-
2、调用
set(T value)
方法往threadLocal
中添加值public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }
-
通过
set(T value)
方法的源码,我们发现会先调用getMap(Thread t)
方法,然后判断该方法返回的 ThreadLocalMap 是否不为 null,如果不为 null,则直接覆盖值;如果为 null ,则再调用createMap(Thread t, T firstValue)
方法来创建一个ThreadLocalMap
,并将其赋值给当前线程的inheritableThreadLocals
属性 -
由于我们创建的是一个 InheritableThreadLocal 对象,而 InheritableThreadLocal 正好重写了
getMap(Thread t)
和createMap(Thread t, T firstValue)
这两个方法,也就是说,调用set(T value)
方法时,内部调用的都是 InheritableThreadLocal 中重写的方法ThreadLocalMap getMap(Thread t) { return t.inheritableThreadLocals; }
void createMap(Thread t, T firstValue) { t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue); }
-
-
3、调用
get()
方法从threadLocal
中获取值public T get() { Thread t = Thread.currentThread(); 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(); }
-
通过
get()
方法的源码,我们会发现会先调用getMap(Thread t)
方法 (即获取的是当前线程inheritableThreadLocals
属性中的值),然后判断该方法返回的 ThreadLocalMap 是否不为 null,如果不为 null,则以当前的 ThreadLocal 作为 key,获取该 ThreadLocalMap 中存储的 Entry;然后再判断该 Entry 是否不为 null,如果不为 null,则获取 Entry 中存储的 value 并返回 -
如果上述条件都不满足,则调用
setInitialValue()
方法来获取 value 并返回private T setInitialValue() { T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); return value; }
protected T initialValue() { return null; }
-
由于我们创建的是一个 InheritableThreadLocal 对象,而 InheritableThreadLocal 正好重写了
getMap(Thread t)
和createMap(Thread t, T firstValue)
这两个方法,也就是说,调用get()
方法时,内部调用的也都是 InheritableThreadLocal 中重写的方法
-
-
三、举例说明
-
例子一
public class ThreadLocalTest { private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>(); public static void main(String[] args){ threadLocal.set(123); System.out.println("parent thread 【" + threadLocal.get() + "】"); new Thread(() -> System.out.println("child thread 【" + threadLocal.get() + "】")).start(); threadLocal.remove(); // parent thread 【123】 // child thread 【null】 } }
-
如上所示,我们使用 ThreadLocal 来进行存储和读取,首先我们调用
set(T value)
方法往当前线程 (主线程) 的threadLocals
中添加值123
,接着我们调用get()
方法从主线程的threadLocals
中取值,由于此时仍然为主线程,所以取值成功 -
然后我们 new 了一个新的线程,由它去调用
get()
方法从自己的threadLocals
中取值,不过此时当前线程已经由主线程变成了 new 出来的新线程,而新线程的threadLocals
中并没有值,所以取值失败
-
-
例子二
public class ThreadLocalTest { private static ThreadLocal<Integer> threadLocal = new InheritableThreadLocal<>(); public static void main(String[] args){ threadLocal.set(123); System.out.println("parent thread 【" + threadLocal.get() + "】"); new Thread(() -> System.out.println("child thread 【" + threadLocal.get() + "】")).start(); threadLocal.remove(); // parent thread 【123】 // child thread 【123】 } }
-
如上所示,我们使用 InheritableThreadLocal 来进行存储和读取,首先我们调用
set(T value)
方法往当前线程 (主线程) 的threadLocals
中添加值123
,接着我们调用get()
方法从主线程的threadLocals
中取值,由于此时仍然为主线程,所以取值成功 -
然后我们 new 了一个新的线程,由它去调用
get()
方法从自己的threadLocals
中取值,不过此时当前线程已经由主线程变成了 new 出来的新线程,与例子一不同的是,由于我们使用的是 InheritableThreadLocal,因此新线程的threadLocals
会去继承父线程 (主线程)threadLocals
中的相关变量,所以取值成功
-