参考:https://blog.csdn.net/upgroup/article/details/107935832
主要从四个问题了解
1)ThreadLocal可以用来解决什么问题,你工作中或者使用的框架中哪里用到了?
2)ThreadLocal解决不了什么问题?怎么办
3)InheritThreadLocal解决不了什么问题,怎么办
4)阿里的TransmittableThreadLocal是如何解决这个问题的
ThreadLocal可以用来解决什么问题
一个变量的值想要在线程中传递,比如说分布式调用跟踪系统中的traceID,RPC框架中传递上下文的context
保证线程不安全的类的安全,比如说SimpleDateFormate
ThreadLocal解决不了问题怎么办
ThreadLocal只是线程传递,但是当在本线程中创建了一个新的线程,比如说又new了一个Thread的情况下,就不能把ThreadLocal中的数据传递给子线程。此时解决办法是通过InheritThreadLocal来解决
InheritThreadLocal解决不了什么问题,怎么办
inheritThreadlocal解决了创建新的子线程的传递问题,但是如果用线程池创建线程的话,使用InheritThreadLocal可以保证数据的传递。但是线程池中的线程是重复使用的,当重复使用线程的时候,重复使用的线程中的InheritThreadLocal仍然是上次创建的数据。解决办法就是阿里的TransmittableThreadLocal
阿里的TransmittableThreadLocal是如何解决这个问题的
阿里的解决方式仍然是使用的InheritThreadLocal,不同的是,阿里通过Javaagent修改了线程池的字节码,在线程池创建Runnable或者Callable的时候进行了包装,我们就叫RunnableWrapper。把需要传递的数据在new RunnableWrapper的时候就传递到了RunnableWrapper的成员变量中。在RunnableWrapper执行run方法的时候,先将成员变量的数据重新放一遍ThreadLocal,然后再真正执行被包装的Runnable的run方法。这样在真正的run方法中就可以拿到ThreadLocal的数据
inheritThreadlocal详解
inheritThreadlocal类
重写了ThreadLocal的三个函数
/**
* 该函数在父线程创建子线程,向子线程复制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);
}
线程间传值实现原理
首先讲解一下Thread类
public class Thread implements Runnable {
......(其他源码)
/*
* 当前线程的ThreadLocalMap,主要存储该线程自身的ThreadLocal
*/
ThreadLocal.ThreadLocalMap threadLocals = null;
/*
* InheritableThreadLocal,自父线程集成而来的ThreadLocalMap,
* 主要用于父子线程间ThreadLocal变量的传递
* 本文主要讨论的就是这个ThreadLocalMap
*/
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
......(其他源码)
}
Thread类包含Threadlocals和inherittableThreadLocals两个变量inheritableThreadLocals 即主要存储可自动向子线程中传递的ThreadLocal.ThreadLocalMap。
接下来看一下父线程创建子线程的流程,我们从最简单的方式说起:
用户创建Thread
Thread thread = new Thread();
Thread创建
/**
* Allocates a new {@code Thread} object. This constructor has the same
* effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
* {@code (null, null, gname)}, where {@code gname} is a newly generated
* name. Automatically generated names are of the form
* {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer.
*/
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
Thread初始化
/**
* 默认情况下,设置inheritThreadLocals可传递
*/
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
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);
......(其他代码)
}
可以看到,采用默认方式产生子线程时,inheritThreadLocals=true;若此时父线程inheritableThreadLocals不为空,则将父线程inheritableThreadLocals传递至子线程。
ThreadLocal.createInheritedMap
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
return new ThreadLocalMap(parentMap);
}
/**
* 构建一个包含所有parentMap中Inheritable ThreadLocals的ThreadLocalMap
* 该函数只被 createInheritedMap() 调用.
*/
private ThreadLocalMap(ThreadLocalMap parentMap) {
Entry[] parentTable = parentMap.table;
int len = parentTable.length;
setThreshold(len);
// ThreadLocalMap 使用 Entry[] table 存储ThreadLocal
table = new Entry[len];
// 逐一复制 parentMap 的记录
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) {
// 可能会有同学好奇此处为何使用childValue,而不是直接赋值,
// 毕竟childValue内部也是直接将e.value返回;
// 个人理解,主要为了减轻阅读代码的难度
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++;
}
}
}
}
子线程将parentMap逐一复制到自身线程
InheritableThreadLocal主要用于子线程创建时,需要自动继承父线程的ThreadLocal变量,方便必要信息的进一步传递。
TransmittableThreadLocal详解
用于解决使用线程池缓存线程的组件的情况下传递ThreadLocal
使用场景:分布式跟踪系统,应用容器或上下层框架跨应用代码给下层SDK传递信息,日志收集系统上下文,
源码分析:
TransmittableThreadLocal 继承自 InheritableThreadLocal,这样可以在不破坏ThreadLocal 本身的情况下,使得当用户利用 new Thread() 创建线程时仍然可以达到传递InheritableThreadLocal 的目的。
public class TransmittableThreadLocal<T> extends InheritableThreadLocal<T>{......}
TransmittableThreadLocal相比于InheritableThreadLocal比较关键的一点是引入了holder变量,这样就不必对外暴露Thread中的inherittablethreadlocals变量保存ThreadLocalMap的封装性
// 理解holder,需注意如下几点:
// 1、holder 是 InheritableThreadLocal 变量;
// 2、holder 是 static 变量;
// 3、value 是 WeakHashMap;
// 4、深刻理解 ThreadLocal 工作原理;
private static InheritableThreadLocal<Map<TransmittableThreadLocal<?>, ?>> holder =
new InheritableThreadLocal<Map<TransmittableThreadLocal<?>, ?>>() {
@Override
protected Map<TransmittableThreadLocal<?>, ?> initialValue() {
return new WeakHashMap<>();
}
@Override
protected Map<TransmittableThreadLocal<?>, ?> childValue(Map<TransmittableThreadLocal<?>, ?> parentValue) {
return new WeakHashMap<>(parentValue);
}
};
// 调用 get() 方法时,同时将 this 指针放入 holder
public final T get() {
T value = super.get();
if (null != value) {
addValue();
}
return value;
}
void addValue() {
if (!holder.get().containsKey(this)) {
holder.get().put(this, null); // WeakHashMap supports null value.
}
}
// 调用 set() 方法时,同时处理 holder 中 this 指针
public final void set(T value) {
super.set(value);
if (null == value) { // may set null to remove value
removeValue();
} else {
addValue();
}
}
void removeValue() {
holder.get().remove(this);
}
个人感觉是在inheritThreadlocal里面在加入一个inheritThreadLocal变量对Threadlocals变量进行封装,保证线程安全
工作流程简介
自定义TtlRunnable实现Runnable,TtlRunnable初始化方法中保持当前线程中已有的TransmittableThreadLocal
private TtlRunnable(Runnable runnable, boolean releaseTtlValueReferenceAfterRun) {
this.copiedRef = new AtomicReference<Map<TransmittableThreadLocal<?>, Object>>(TransmittableThreadLocal.copy());
this.runnable = runnable;
this.releaseTtlValueReferenceAfterRun = releaseTtlValueReferenceAfterRun;
}
线程池中线程调用run方法,执行前先backup holder中所有的TransmittableThreadLocal, copiedRef中不存在,holder存在的,说明是后来加进去的,remove掉holder中的;将copied中的TransmittableThreadLocal set到当前线程中
public void run() {
Map<TransmittableThreadLocal<?>, Object> copied = copiedRef.get();
if (copied == null || releaseTtlValueReferenceAfterRun && !copiedRef.compareAndSet(copied, null)) {
throw new IllegalStateException("TTL value reference is released after run!");
}
Map<TransmittableThreadLocal<?>, Object> backup = TransmittableThreadLocal.backupAndSetToCopied(copied);
try {
runnable.run();
} finally {
TransmittableThreadLocal.restoreBackup(backup);
}
}
执行后再恢复 backup 的数据到 holder 中(backup中不存在,holder中存在的TransmittableThreadLocal,从holder中remove掉),将 backup 中的 TransmittableThreadLocal set到当前线程中