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);
}
//ThreadLocalMap保存在Thread中:
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
可见threadLocals为Thread的内部变量,每个Thread类的实例,都有自己的threadLocals变量
// Thread类源码
public class Thread implements Runnable {
......
ThreadLocal.ThreadLocalMap threadLocals = null;
......
}
总结
ThreadLocal属性保存在每个线程实例中,线程实例通过Map结构管理所有的ThreadLocal属性,Map的key为ThreadLocal属性。
InheritableThreadLocal实现原理
ThreadLocal属性是Thread的内部变量,父子线程是不同的线程实例,显然在子线程中无法获取父线程设置的属性:
public class MainToChild implements Runnable {
public static ThreadLocal<String> threadLocal = new ThreadLocal();
@Override
public void run() {
System.out.println("child : " + Thread.currentThread().getName());
System.out.println("child : " + threadLocal.get());
}
public static void main(String[] args) {
MainToChild mainToChild = new MainToChild();
Thread thread = new Thread(mainToChild);
thread.start();
threadLocal.set("main");
System.out.println("main : " + Thread.currentThread().getName());
System.out.println("main : " + threadLocal.get());
}
}
---------------------------------------------
main : main
child : Thread-0
child : null
main : main
ThreadLocal属性是Thread的内部变量,父子线程是不同的线程实例,显然在子线程中无法获取父线程设置的属性
InheritableThreadLocal实现父子进程传递
public class MainToChild implements Runnable {
public static InheritableThreadLocal<String> threadLocal = new InheritableThreadLocal();
@Override
public void run() {
System.out.println("child name : " + Thread.currentThread().getName());
System.out.println("child get main value : " + threadLocal.get());
threadLocal.set("child");
System.out.println("child value : " + threadLocal.get());
}
public static void main(String[] args) {
threadLocal.set("main");
MainToChild mainToChild = new MainToChild();
Thread thread = new Thread(mainToChild);
thread.start();
System.out.println("main name : " + Thread.currentThread().getName());
System.out.println("main value: " + threadLocal.get());
}
}
---------------------------------------------
main name : main
child name : Thread-0
child get main value : main
main value: main
child value : child
InheritableThreadLocal改变初始值后子进程无法获取新值
package com.sankuai.mall.consumerapi.web.controller;
import lombok.SneakyThrows;
/**
* @author : mazhen
* Date : 2021/11/12
* Time : 3:11 下午
* ---------------------------------------
* Desc :
*/
public class MainToChild implements Runnable {
public static InheritableThreadLocal threadLocal = new InheritableThreadLocal();
@SneakyThrows
@Override
public void run() {
System.out.println("child name : " + Thread.currentThread().getName());
System.out.println("1 child get main value : " + threadLocal.get());
System.out.println("2 child get main value : " + threadLocal.get());
}
public static void main(String[] args) {
threadLocal.set("main");
MainToChild mainToChild = new MainToChild();
Thread thread = new Thread(mainToChild);
thread.start();
threadLocal.set("again main");
System.out.println("main name : " + Thread.currentThread().getName());
System.out.println("main value: " + threadLocal.get());
}
}
子进程无法获取新值
---------------------------------------------
child name : Thread-0
main name : main
1 child get main value : main
main value: again main
2 child get main value : main
改为线程池的方式也是一样的效果
package com.sankuai.mall.consumerapi.web.controller;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author : mazhen
* Date : 2021/11/12
* Time : 4:11 下午
* ---------------------------------------
* Desc :
*/
public class ITL {
public static InheritableThreadLocal<String> param = new InheritableThreadLocal<>();
public static void main(String[] args) {
param.set("main");
ExecutorService pool = Executors.newFixedThreadPool(1);
pool.submit(() -> {
System.out.println(param.get());
});
param.set("main2");
pool.submit(() -> {
System.out.println(param.get());
});
pool.submit(() -> {
System.out.println(param.get());
});
pool.shutdown();
}
}
线程池只能获取初始值,无法获取新值
---------------------------------------------
main
main
main
InheritableThreadLocal 实现原理
// Thread类源码
public class Thread implements Runnable {
......
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
......
}
数据结构ThreadLocalMap和threadLocal是一样的
InheritableThreadLocal及其局限性
Jdk提供了InheritableThreadLocal类,用于在父子线程间传递线程变量(ThreadLocal),实现原理就是在Thread类保存名为inheritableThreadLocals的成员属性(以InheritableThreadLocal对象为Key的ThreadLocalMap),并在初始化创建子线程时,将父线程的inheritableThreadLocals赋给子线程,这部分逻辑在Thread.init()方法内。
//只会在初始化一次,设置为父类的变量值
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);
.....
}
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
return new ThreadLocalMap(parentMap);
}
//初始化时会把父类的线程变量遍历的拷贝过来
private ThreadLocalMap(ThreadLocalMap parentMap) {
Entry[] parentTable = parentMap.table;//父类的table
int len = parentTable.length;
setThreshold(len);
table = new Entry[len];//当前类的table长度和父类相同
for (int j = 0; j < len; j++) {
Entry e = parentTable[j]; //遍历父类table
if (e != null) {
@SuppressWarnings("unchecked")
ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
if (key != null) {
// 需要注意childValue方法,通过重写这个方法,
// 可以实现自定义的拷贝逻辑
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++;
}
}
}
}
这种方式在线程只被创建和使用一次时是有效的,但对于使用线程池的场景下,由于线程被复用,初始化一次后,后续修改变量,子线程并不会再更新初始值,导致后续提交的任务并不会获取到父线程的线程变量最新值,同时,还会获取到当前线程之前的初始变量值。