1 concept
In Java
multi-threaded programming, through the ThreadLocal
realization of a thread-local variable mechanism through space for time to achieve the purpose thread isolation.
But now there is such a demand: a parent thread in thread local variables need to be passed to the child threads.
For this case, ThreadLocal
it can not be completed because the ThreadLocal
preservation of thread-local variables, the parent thread of thread-local variables only he can get, can not get the child thread. The following code demonstrates:
package com.tao.springbootdemo.thread;
public class ParentAndChildThreadMain {
public static void main(String[] args) {
// 父线程
Thread parentThread = new Thread(new Runnable() {
// 父线程中的线程局部变量
private ThreadLocal<String> threadLocal = new ThreadLocal<>();
// private InheritableThreadLocal<String> threadLocal = new InheritableThreadLocal<>();
@Override
public void run() {
System.out.println("> 父线程中设置线程的本地变量");
threadLocal.set("local variable");
System.out.println("> 父线程中拿到的线程本地变量是:" + threadLocal.get());
// 在父线程中再起一个子线程
Thread childThread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("> 子线程中获取父线程的本地变量");
System.out.println("> 子线程中拿到的父线程本地变量是:" + threadLocal.get());
}
});
childThread.setName("子线程!");
childThread.start();
}
});
parentThread.setName("父线程!");
parentThread.start();
}
}
The output was:
> 父线程中设置线程的本地变量
> 父线程中拿到的线程本地变量是:local variable
> 子线程中获取父线程的本地变量
> 子线程中拿到的父线程本地变量是:null
Process finished with exit code 0
So, how to solve this demand?
Java
Provides a InheritableThreadLocal
solved problem thread local variables passed in the parent-child thread! ! !
The following code demonstrates:
package com.tao.springbootdemo.thread;
public class ParentAndChildThreadMain {
public static void main(String[] args) {
// 父线程
Thread parentThread = new Thread(new Runnable() {
// 父线程中的线程局部变量
// private ThreadLocal<String> threadLocal = new ThreadLocal<>();
private InheritableThreadLocal<String> threadLocal = new InheritableThreadLocal<>();
@Override
public void run() {
System.out.println("> 父线程中设置线程的本地变量");
threadLocal.set("local variable");
System.out.println("> 父线程中拿到的线程本地变量是:" + threadLocal.get());
// 在父线程中再起一个子线程
Thread childThread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("> 子线程中获取父线程的本地变量");
System.out.println("> 子线程中拿到的父线程本地变量是:" + threadLocal.get());
}
});
childThread.setName("子线程!");
childThread.start();
}
});
parentThread.setName("父线程!");
parentThread.start();
}
}
The output obtained is:
> 父线程中设置线程的本地变量
> 父线程中拿到的线程本地变量是:local variable
> 子线程中获取父线程的本地变量
> 子线程中拿到的父线程本地变量是:local variable
Process finished with exit code 0
We just ThreadLocal
changed InheritableThreadLocal
, you will be able to take the child thread to thread-local variables of the parent thread.
2 Principle Analysis
2.1 class source code analysis
First look Thread
like:
public class Thread implements Runnable {
......
/**
* 与此线程相关的ThreadLocal值,保存线程本地变量。
* 这个map由ThreadLocal类维护。
*/
ThreadLocal.ThreadLocalMap threadLocals = null;
/*
* 与此线程相关的那些从父线程继承而来的ThreadLocal值。
* 这个map由InheritableThreadLocal类维护。
*/
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
As can be seen, threadLocals
and inheritableThreadLocals
maintained by two instance fields, with each other independently of each other .
Then, look at InheritableThreadLocal
the source code:
public class InheritableThreadLocal<T> extends ThreadLocal<T> {
/**
* 该函数在父线程创建子线程,向子线程复制InheritableThreadLocal变量时使用
*/
protected T childValue(T parentValue) {
return parentValue;
}
/**
* 由于重写了getMap,操作InheritableThreadLocal时,
* 将只影响Thread类中的inheritableThreadLocals变量,
* 与threadLocals变量不再有关系。
*/
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
/**
* 类似于getMap,操作InheritableThreadLocal时,
* 将只影响Thread类中的inheritableThreadLocals变量,
* 与threadLocals变量不再有关系。
*/
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
}
InheritableThreadLocal
Inherited fromThreadLocal
;- Rewriting the
ThreadLocal
three functions; - Rewritten
getMap()
,createMap()
function is very important, the operation ist.inheritableThreadLocals
, these two functions in the callset(T value)
andget()
when the function has played a very important role; - And
ThreadLocal
affect each other.
2.2 by value analysis
First, create a child thread starts from the parent thread.
Parent thread calls new Thread()
to create a child thread:
Thread childThread = new Thread();
Call the Thread
constructor of the class:
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
// 默认情况下,设置inheritThreadLocals为可传递
init(g, target, name, stackSize, null, true);
}
/**
* 初始化一个线程。
* 此函数有两处调用:
* 1、上面的 init(),不传AccessControlContext,inheritThreadLocals = true;
* 2、传递AccessControlContext,inheritThreadLocals = false
*/
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);
......(其他代码)
}
-
It can be seen when generating a sub-thread default mode
inheritThreadLocals = true
. -
If at this time the parent thread
inheritableThreadLocals
is not empty, then the parent threadinheritableThreadLocals
to child thread passes. If the parent thread is usedInheritableThreadLocal
to save the thread local variable, then callset(T value)
time, the operation is the threadinheritableThreadLocals
, soparent.inheritableThreadLocals != null
. -
Which, by calling
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals)
to the parent threadinheritableThreadLocals
pass and create a child threadinheritableThreadLocals
.
View ThreadLocal.createInheritedMap
Source function:
// 创建线程的inheritableThreadLocals
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
return new ThreadLocalMap(parentMap);
}
/**
* 接收传进来的 parent.inheritableThreadLocals,
* 构建一个包含parent.inheritableThreadLocals中所有键值对的ThreadLocalMap,
* 该函数只被 createInheritedMap() 调用.
*/
private ThreadLocalMap(ThreadLocalMap parentMap) {
Entry[] parentTable = parentMap.table;
int len = parentTable.length;
setThreshold(len);
table = new Entry[len];
// 逐个复制parent.inheritableThreadLocals中的Entry
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
Entry c = new Entry(key, value);
int h = key.threadLocalHashCode & (len - 1);
while (table[h] != null)
h = nextIndex(h, len);
// 保存
table[h] = c;
size++;
}
}
}
}
From the top of the source code analysis can be seen in the creation of the child thread, the parent thread in inheritableThreadLocals
the corresponding ThreadLocalMap
all in Entry
all copied to a new ThreadLocalMap
, and finally this will ThreadLocalMap
be assigned to the child threads inheritableThreadLocals
.
When you call the parent thread in the child thread InheritableThreadLocal<String> threadLocal
the threadLocal.get()
way to get the parent thread thread-local variable, view the source code:
public T get() {
Thread t = Thread.currentThread();
// InheritableThreadLocal重写了getMap,返回的是线程的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();
}
As can be seen,
- Because
InheritableThreadLocal
rewrite the classgetMap()
, it returns the threadinheritableThreadLocals
. - So, in the sub-thread call
get()
to get the current thread isinheritableThreadLocals
thisThreadLocalMap
. - Finally,
get()
the return value is stored in a sub-thread ofinheritableThreadLocals
values.
2.3 child thread will be updated passed from parent thread to thread local variables do?
Write code to test:
package com.tao.springbootdemo.thread;
public class ParentAndChildThreadMain {
public static void main(String[] args) {
// 父线程
Thread parentThread = new Thread(new Runnable() {
// 父线程中的线程局部变量
// private ThreadLocal<String> threadLocal = new ThreadLocal<>();
private InheritableThreadLocal<String> threadLocal = new InheritableThreadLocal<>();
@Override
public void run() {
System.out.println("> 父线程中设置线程的本地变量");
threadLocal.set("local variable");
System.out.println("> 父线程中拿到的线程本地变量是:" + threadLocal.get());
// 在父线程中再起一个子线程
Thread childThread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("> 子线程中获取父线程的本地变量");
System.out.println("> 子线程中拿到的父线程本地变量是:" + threadLocal.get());
try {
System.out.println("> 子线程 sleep 5秒");
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("> 子线程中再次获取父线程的本地变量");
System.out.println("> 子线程中拿到的父线程本地变量是:" + threadLocal.get());
}
});
childThread.setName("子线程!");
childThread.start();
try {
System.out.println("> 父线程 sleep 2秒");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("> 父线程中再次设置线程的本地变量");
threadLocal.set("AAA");
System.out.println("> 父线程中拿到的改变后的线程本地变量是:" + threadLocal.get());
}
});
parentThread.setName("父线程!");
parentThread.start();
}
}
Program output:
> 父线程中设置线程的本地变量
> 父线程中拿到的线程本地变量是:local variable
> 父线程 sleep 2秒
> 子线程中获取父线程的本地变量
> 子线程中拿到的父线程本地变量是:local variable
> 子线程 sleep 5秒
> 父线程中再次设置线程的本地变量
> 父线程中拿到的改变后的线程本地变量是:AAA
> 子线程中再次获取父线程的本地变量
> 子线程中拿到的父线程本地变量是:local variable
Process finished with exit code 0
You can see, the child thread is not updated passed from parent thread over the thread-local variables! ! !
3 summary
InheritableThreadLocal
Mainly used in the creation of the child thread, the thread local variables need to automatically inherit parent thread to use.