1.ThreadLocal概述
ThreadLocal的作用是提供线程内的局部变量,这种变量在多线程环境下访问时能够保证各个线程里变量的独立性。线程局部变量(ThreadLocal)其实的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是Java中一种较为特殊的线程绑定机制,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。
2.ThreadLocal数据结构图
3.ThreadLocal、Thread、ThreadLocalMap、Entry之间的关系
4.set方法源码解析
public void set(T value) {
//获取当前线程
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
//map即Thread中threadLocals属性
if (map != null) {
//将键值对保存到map中,map中的key为this,就是调用该方法的ThreadLocal对象
map.set(this, value);
} else {
//创建一个新的ThreadLocalMap
createMap(t, value);
}
}
ThreadLocal.getMap
//获取Thread中threadLocals属性,该属性的类型是ThreadLocal.ThreadLocalMap
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
ThreadLocal.createMap
//首次设值需要先创建新的ThreadLocalMap
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
ThreadLocal.ThreadLocalMap构造函数
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
//初始化entry
table = new Entry[INITIAL_CAPACITY];
//key和16逻辑与运算
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
//创建entry节点,设置key-value
table[i] = new Entry(firstKey, firstValue);
size = 1;
//设置扩容阈值
setThreshold(INITIAL_CAPACITY);
}
ThreadLocal.ThreadLocalMap.set
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
//计算出索引的位置
int i = key.threadLocalHashCode & (len-1);
//从索引位置开始遍历,由于不是不是链表结构,所以通过nextIndex方法来寻找下一个索引位置
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
//entry的key,也就是ThreadLocal
ThreadLocal<?> k = e.get();
//如果entry的可key和传入的key相等,则替换掉value的值
if (k == key) {
e.value = value;
return;
}
//如果Entry的key为空,则表示该Entry需要被清空
if (k == null) {
//该方法会继续寻找传入key的安放位置,并清理掉key为空的Entry
replaceStaleEntry(key, value, i);
return;
}
}
//位置i是一个空位置,放置在该位置上
tab[i] = new Entry(key, value);
int sz = ++size;
//cleanSomeSlots是用来清理key为null的Entry,如果此方法返回true,说明至少清理了一个元素,则此次set不需要扩容,如果此方法返回false,则判断sz是否大于阈值
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();//扩容
}
ThreadLocal.ThreadLocalMap.replaceStaleEntry
- slotToExpunge始终记录着需要清除的元素的最前面的位置(即slotToExpunge前面的元素是不需要清除的)
- 从位置staleSlot向前遍历,直到遇到Entry为空,用staleSlot记录最后一个key为null的索引位置(也就是遍历过位置最前的key为null的位置)
- 从位置staleSlot向后遍历,直到遇到Entry为空,如果遍历到key和入参key相同的,则将入参的value替换掉该Entry的value,并将i位置和staleSlot位置的元素对换(staleSlot位置较前,是要清除的元素),遍历的时候判断slotToExpunge的值是否需要调整,最后调用expungeStaleEntry方法(见下文expungeStaleEntry方法详解)和cleanSomeSlots方法(见下文)清除key为null的元素。
- 如果key没有找到,则使用入参的key和value新建一个Entry,放在staleSlot位置
- 判断是否还有其他位置的元素key为null,如果有则调用expungeStaleEntry方法和cleanSomeSlots方法清除key为null的元素
//该方法的作用是替换待清除的entry,staleSlot为需要清理掉的Entry的索引
//key:新的key值 value:新的value值 staleSlot:待清除的元素下标
private void replaceStaleEntry(ThreadLocal<?> key, Object value,
int staleSlot) {
Entry[] tab = table;
int len = tab.length;
Entry e;
//清除元素的开始位置
int slotToExpunge = staleSlot;
//向前遍历,直到遇到Entry为空
for (int i = prevIndex(staleSlot, len);
(e = tab[i]) != null;
i = prevIndex(i, len))
if (e.get() == null)
//记录最后一个索引为空的位置
slotToExpunge = i;
//向后遍历,直到entry为空
for (int i = nextIndex(staleSlot, len);
(e = tab[i]) != null;
i = nextIndex(i, len)) {
ThreadLocal<?> k = e.get();
//该Entry的key和传入的key相等,则将传入的value替换
if (k == key) {
e.value = value;
//将i位置和staleSlot的元素对换(staleSlot位置较前,是要被清除的元素)
tab[i] = tab[staleSlot];
tab[staleSlot] = e;
//如果相等,说明向前遍历没有找到为空的key,即staleSlot的前面没有需要清除的
//因为原staleSlot的元素已经被放到了i位置了,这时i前面的元素都不需要清除
if (slotToExpunge == staleSlot)
slotToExpunge = i;
//从slotToExpunge位置开始清除Key为空的Entry
cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
return;
}
//如果第一次遍历到Key为null的元素,并且上面向前寻找key为null遍历没有找到,将slotToExpunge设置为当前的位置
if (k == null && slotToExpunge == staleSlot)
slotToExpunge = i;
}
tab[staleSlot].value = null;
//如果key没有找到,则新建一个Entry,放在staleSlot的位置
tab[staleSlot] = new Entry(key, value);
//如果slotToExpunge != staleSlot,代表除了staleSlot位置还有其他位置的元素需要清除
//需要清除的定义:key为null的Entry
if (slotToExpunge != staleSlot)
cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
}
replaceStaleEntry流程图
ThreadLocal.ThreadLocalMap.expungeStaleEntry
从staleSlot开始,清除key为null的Entry,并将不为空的元素放到合适的位置,最后遍历到Entry为空的元素时,跳出循环返回当前索引位置。
private int expungeStaleEntry(int staleSlot) {
Entry[] tab = table;
int len = tab.length;
// 将tab上staleSlot位置的元素清空
tab[staleSlot].value = null;
tab[staleSlot] = null;
size--;
// Rehash until we encounter null
Entry e;
int i;
//遍历下一个元素,遍历到Entry为null是跳出循环,并返回索引位置
for (i = nextIndex(staleSlot, len);
(e = tab[i]) != null;
i = nextIndex(i, len)) {
ThreadLocal<?> k = e.get();
//Entry的key为空时,将该位置的对象清空
if (k == null) {
e.value = null;
tab[i] = null;
size--;
} else {
//Entry的key不为空时,重新计算Entry的索引位置
int h = k.threadLocalHashCode & (len - 1);
//如果索引位置不等于当前索引位置,则将i对象清空,替当前Entry寻找正确的位置
//h!=i是由于开放地址法,放到了别的位置上
//为什么要这么做呢?主要是开放地址寻找元素的时候,遇到null就停止寻找了,由于上面if代码块中,key==null的时候已经设置entry为null了,不移动的话后面的元素就访问不到了
if (h != i) {
tab[i] = null;
//如果h位置不为空,说明哈希是由冲突的,则向后寻找当前Entry的位置
while (tab[h] != null)
h = nextIndex(h, len);
tab[h] = e;
}
}
}
return i;
}
expungeStaleEntry流程图
ThreadLocal.ThreadLocalMap.cleanSomeSlots
从 i 开始,清除key为空的Entry,遍历次数由当前的table长度决定,当遍历到一个key为null的元素时,调用expungeStaleEntry清除,并将遍历次数重置。至于为什么使用table长度来决定遍历次数,官方给出的解释是这个方法简单、快速,并且效果不错。
private boolean cleanSomeSlots(int i, int n) {
boolean removed = false;
Entry[] tab = table;
int len = tab.length;
do {
i = nextIndex(i, len);//下一个索引位置
Entry e = tab[i];
if (e != null && e.get() == null) {//遍历key为null的元素
n = len;//重置n的值
removed = true;//标志有移除元素
i = expungeStaleEntry(i);//移除i位置之后key为null的元素
}
} while ( (n >>>= 1) != 0);//判断(n=n/2) != 0
return removed;
}
cleanSomeSlots流程图
ThreadLocal.ThreadLocalMap.prevIndex
//获得入参i(数组index)的前一个index,如果i=0,则它的index就是整个数组的队尾index,即index=len-1
private static int prevIndex(int i, int len) {
return ((i - 1 >= 0) ? i - 1 : len - 1);
}
ThreadLocal.ThreadLocalMap.nextIndex
//获得入参i(数组index)的后一个index,如果i=队尾index,则它的index就是整个数组的队首index,即index=0
private static int nextIndex(int i, int len) {
return ((i + 1 < len) ? i + 1 : 0);
}
ThreadLocal.ThreadLocalMap.rehash
private void rehash() {
//清除陈旧entry
expungeStaleEntries();
// Use lower threshold for doubling to avoid hysteresis(使用较低阈值加倍以避免滞后)
if (size >= threshold - threshold / 4)
resize();
}
rehash流程图
ThreadLocal.ThreadLocalMap.expungeStaleEntries
//遍历table数组里的Entry,调用expungeStaleEntry方法
private void expungeStaleEntries() {
Entry[] tab = table;
int len = tab.length;
for (int j = 0; j < len; j++) {
Entry e = tab[j];
if (e != null && e.get() == null)
expungeStaleEntry(j);
}
}
ThreadLocal.ThreadLocalMap.resize
private void resize() {
Entry[] oldTab = table;
int oldLen = oldTab.length;
//将原数组大小扩容2倍
int newLen = oldLen * 2;
Entry[] newTab = new Entry[newLen];
int count = 0;
for (Entry e : oldTab) {
if (e != null) {
ThreadLocal<?> k = e.get();
if (k == null) {
e.value = null; // Help the GC
} else {
//计算旧的entry在新table数组中的位置h
int h = k.threadLocalHashCode & (newLen - 1);
//如果h位置被其他entry占据了,那么就向后查找空位,直到找到为止
while (newTab[h] != null)
h = nextIndex(h, newLen);
newTab[h] = e;
count++;
}
}
}
//因为使用的新数组,所以设置一下新table的阈值
setThreshold(newLen);
size = count;
table = newTab;
}
5.get方法源码
public T get() {
Thread t = Thread.currentThread();
//获取Thread中threadLocals属性,该属性的类型是ThreadLocal.ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null) {
//通过调用get方法获取对应的Entry
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
//该线程的ThreadLocalMap为空,初始化一个
return setInitialValue();
}
ThreadLocal.ThreadLocalMap.getEntry
//通过调用get方法获取对应的Entry
private Entry getEntry(ThreadLocal<?> key) {
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
//如果该entry的key和传入的key相等,则为目标entry直接返回
if (e != null && e.get() == key)
return e;
else
//否则e不是目标entry,从e之后继续寻找目标entry
return getEntryAfterMiss(key, i, e);
}
ThreadLocal.ThreadLocalMap.setInitialValue
private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
Entry[] tab = table;
int len = tab.length;
while (e != null) {
ThreadLocal<?> k = e.get();
//找到目标entry,直接返回
if (k == key)
return e;
//key==null,调用expungeStaleEntry清除
if (k == null)
expungeStaleEntry(i);
else
i = nextIndex(i, len);//下一个索引位置
e = tab[i];//下一个遍历的entry
}
return null;//找不返回空
}
ThreadLocal.ThreadLocalMap.setInitialValue
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);
}
if (this instanceof TerminatingThreadLocal) {
TerminatingThreadLocal.register((TerminatingThreadLocal<?>) this);
}
return value;
}
6.remove方法源码
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null) {
m.remove(this);
}
}
ThreadLocal.ThreadLocalMap.remove
private void remove(ThreadLocal<?> key) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);//计算出索引位置
//从i开始遍历,直到entry为空
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
if (e.get() == key) {
e.clear();//将key的引用清空
expungeStaleEntry(i);//调用expungeStaleEntry方法清除key为null的Entry
return;
}
}
}
7.ThreadLocal面试题
为什么要使用key.threadLocalHashCode & (len-1) 计算索引位置?
key.threadLocalHashCode & (len-1) 能让哈希码均匀的分布在2的N次方的数组里,神奇数字:0x61c88647
具体原理不做介绍了
ThreadLocalMap和HashMap的区别是什么?
- 数据结构:ThreadLocalMap仅仅是数组,HashMap是数组+链表
- **哈希冲突:**ThreadLocalMap是通过开放寻址法,HashMap是通过链地址法
- Enry内部类中的引用:ThreadLocalMap中Entry的key是弱引用,HashMap的Entry中都是强引用
什么是开放地址法?
开放地址法的基本思想就是:一旦发生了冲突,那么就去寻找下一个空的地址;那么只要表足够大,空的地址总能找到,并将记录插入进去。
优点: 指针需要额外的空间,故当结点规模较小时,开放定址法较为节省空间
**缺点:**容易产生堆积问题,不适于大规模的数据存储;散列函数的设计对冲突会有很大的影响,插入时可能会出现多次冲突的现象; 删除的元素是多个冲突元素中的一个,需要对后面的元素作处理,实现较复杂。
什么是链地址法?
在hash冲突产生时,将相同具有相同hash值的对象以链表的形式存储,更直白的表述就是数组中的每个元素不在是具体的每个要存储的对象了,每个元素代表具有相同hash值对象组成的链表,通过对象内部的指针可以查询到下一个具有相同hash值的对象。 简单总结:将产生冲突的值以链表的形式连起来。
**优点:**处理冲突简单,且无堆积现象,平均查找长度短;链表中的结点是动态申请的,适合构造表不能确定长度的情况;删除结点的操作易于实现。只要简单地删去链表上相应的结点即可;
**缺点:**指针需要额外的空间,故当结点规模较小时,开放定址法较为节省空间。
ThreadLocalMap采用开放地址法原因是什么?
- ThreadLocal往往存放的数据量不会特别大(而且key 是弱引用又会被垃圾回收,及时让数据量更小)。
- 采用开放地址法简单的结构会更节省空间,同时数组的查询效率也是非常高,加上第一点的保障,冲突概率也比较低。
8.TransmittableThreadLocal的原理分析
简介:
TransmittableThreadLocal是由阿里开发的一个线程变量传递工具包,解决了InheritableThreadLocal只能再new Thread的时候传递本地变量,无法应用到线程池的问题。可以应用来作链路追踪,传递变量等用途
TransmittableThreadLocal 需要配合 TTL 提供的 TtlExecutors、TtlRunnable 和 TtlCallable使用,也可以使用 Java Agent 无侵入式实现线程池的传递。
使用场景:
- 调用链中的 traceId 传递
- 框架层封装的业务工具在业务线程池中传递
- Slf4j 日志框架中 MDC 传递
TTL执行流程:
TransmittableThreadLocal 的构造
//是否要忽略 null value,如果这个参数为 false,则哪怕 value 是 null,也会存储下来
private final boolean disableIgnoreNullValueSemantics;
//这个参数默认为 false
public TransmittableThreadLocal() {
this(false);
}
public TransmittableThreadLocal(boolean disableIgnoreNullValueSemantics) {
this.disableIgnoreNullValueSemantics = disableIgnoreNullValueSemantics;
}
holder变量
private static final InheritableThreadLocal<WeakHashMap<TransmittableThreadLocal<Object>, ?>> holder = new InheritableThreadLocal<WeakHashMap<TransmittableThreadLocal<Object>, ?>>() {
// 复写这个方法应该没有别的深意,只是为了防止在调用 holder.get().xxx() 的时候报空指针
// 应该是开发人员觉得这样比较优雅
protected WeakHashMap<TransmittableThreadLocal<Object>, ?> initialValue() {
return new WeakHashMap();
}
// 这个方法实现了子线程和父线程之间的信息传递
protected WeakHashMap<TransmittableThreadLocal<Object>, ?> childValue(WeakHashMap<TransmittableThreadLocal<Object>, ?> parentValue) {
return new WeakHashMap(parentValue);
}
};
由上述可知:
- holder 是一个记录的 value 是 WeakHashMap 的 InheritableThreadLocal
- WeakHashMap 的 value 并没有被使用到,可以将其视为一个 WeakHashSet
- holder 复写了 initialValue 和 childValue 两个方法
holder 最重要的方法是 addThisToHolder:
// 如果当前 TransmittableThreadLocal 没有被记录在 holder 中,则会在此处 put 进去
private void addThisToHolder() {
if (!((WeakHashMap)holder.get()).containsKey(this)) {
((WeakHashMap)holder.get()).put(this, (Object)null);
}
}
移除方法:
private void removeThisFromHolder() {
((WeakHashMap)holder.get()).remove(this);
}
set方法源码
public final void set(T value) {
if (!this.disableIgnoreNullValueSemantics && null == value) {
// 如果 value 是 null,且不忽略 null value,则此处进入删除逻辑
this.remove();
} else {
// 存储逻辑
super.set(value);
// 将当前的 TransmittableThreadLocal 注册到 holder 里
this.addThisToHolder();
}
}
get方法源码
public final T get() {
T value = super.get();
// 尝试注册到 holder
if (this.disableIgnoreNullValueSemantics || null != value) {
this.addThisToHolder();
}
return value;
}
Snapshot
Snapshot 是 TransmittableThreadLocal 的内部类,用来存放当前线程内的 ThreadLocal 和 TransmittableThreadLocal 数据
private static class Snapshot {
final HashMap<TransmittableThreadLocal<Object>, Object> ttl2Value;
final HashMap<ThreadLocal<Object>, Object> threadLocal2Value;
private Snapshot(HashMap<TransmittableThreadLocal<Object>, Object> ttl2Value, HashMap<ThreadLocal<Object>, Object> threadLocal2Value) {
this.ttl2Value = ttl2Value;
this.threadLocal2Value = threadLocal2Value;
}
}
Transmitter
Transmitter 是 TransmittableThreadLocal 的内部类,本质上是一组静态工具。
获取一个快照
@NonNull
public static Object capture() {
// captureTtlValues() 会将当前线程的 TransmittableThreadLocal 数据做成一个 HashMap
// captureThreadLocalValues() 会将当前线程的 ThreadLocal 数据做成一个 HashMap
return new TransmittableThreadLocal.Transmitter.Snapshot(captureTtlValues(), captureThreadLocalValues());
}
获取 holder 中所有的 TransmittableThreadLocal 数据
private static HashMap<TransmittableThreadLocal<Object>, Object> captureTtlValues() {
HashMap<TransmittableThreadLocal<Object>, Object> ttl2Value = new HashMap();
Iterator var1 = ((WeakHashMap)TransmittableThreadLocal.holder.get()).keySet().iterator();
while(var1.hasNext()) {
TransmittableThreadLocal<Object> threadLocal = (TransmittableThreadLocal)var1.next();
ttl2Value.put(threadLocal, threadLocal.copyValue());
}
return ttl2Value;
}
获取 threadLocalHolder 中所有 ThreadLocal 数据
private static HashMap<ThreadLocal<Object>, Object> captureThreadLocalValues() {
HashMap<ThreadLocal<Object>, Object> threadLocal2Value = new HashMap();
Iterator var1 = threadLocalHolder.entrySet().iterator();
while(var1.hasNext()) {
Entry<ThreadLocal<Object>, TtlCopier<Object>> entry = (Entry)var1.next();
ThreadLocal<Object> threadLocal = (ThreadLocal)entry.getKey();
TtlCopier<Object> copier = (TtlCopier)entry.getValue();
threadLocal2Value.put(threadLocal, copier.copy(threadLocal.get()));
}
return threadLocal2Value;
}
重放
repaly
// 本质上是对一个 snapshot 进行拷贝
@NonNull
public static Object replay(@NonNull Object captured) {
TransmittableThreadLocal.Transmitter.Snapshot capturedSnapshot = (TransmittableThreadLocal.Transmitter.Snapshot)captured;
return new TransmittableThreadLocal.Transmitter.Snapshot(replayTtlValues(capturedSnapshot.ttl2Value), replayThreadLocalValues(capturedSnapshot.threadLocal2Value));
}
replayTtlValues
// 本质上是对一个 map 进行深拷贝
@NonNull
private static HashMap<TransmittableThreadLocal<Object>, Object> replayTtlValues(@NonNull HashMap<TransmittableThreadLocal<Object>, Object> captured) {
// 创建一个新的 map
HashMap<TransmittableThreadLocal<Object>, Object> backup = new HashMap();
Iterator iterator = ((WeakHashMap)TransmittableThreadLocal.holder.get()).keySet().iterator();
while(iterator.hasNext()) {
TransmittableThreadLocal<Object> threadLocal = (TransmittableThreadLocal)iterator.next();
// 将原来的 map 复制到新的 map 中
backup.put(threadLocal, threadLocal.get());
// 此处比较 holder 和 captured 的 key
// 如果对应不一致,则将 holder 里的数据清空
if (!captured.containsKey(threadLocal)) {
iterator.remove();
threadLocal.superRemove();
}
}
// 将 value 和 key 对应起来
// 这是一个保底纠错逻辑
setTtlValuesTo(captured);
// 这是一个暂时没有用的扩展方法
TransmittableThreadLocal.doExecuteCallback(true);
return backup;
}
replayThreadLocalValues
// 本质上是对一个 map 进行深拷贝
private static HashMap<ThreadLocal<Object>, Object> replayThreadLocalValues(@NonNull HashMap<ThreadLocal<Object>, Object> captured) {
HashMap<ThreadLocal<Object>, Object> backup = new HashMap();
Iterator var2 = captured.entrySet().iterator();
while(var2.hasNext()) {
Entry<ThreadLocal<Object>, Object> entry = (Entry)var2.next();
ThreadLocal<Object> threadLocal = (ThreadLocal)entry.getKey();
backup.put(threadLocal, threadLocal.get());
// threadLocalClearMark 是一个空对象,用于占位
// 如果此处的 value 就是这个空对象,则此处代表这个 ttl 里的 value 已经被 clear 了
Object value = entry.getValue();
if (value == threadLocalClearMark) {
threadLocal.remove();
} else {
threadLocal.set(value);
}
}
return backup;
}
恢复
restore
// 用快照来恢复当前线程的 ttl 数据
public static void restore(@NonNull Object backup) {
TransmittableThreadLocal.Transmitter.Snapshot backupSnapshot = (TransmittableThreadLocal.Transmitter.Snapshot)backup;
restoreTtlValues(backupSnapshot.ttl2Value);
restoreThreadLocalValues(backupSnapshot.threadLocal2Value);
}
restoreTtlValues
这个方法与 replayTtlValues(…) 方法比较像
private static void restoreTtlValues(@NonNull HashMap<TransmittableThreadLocal<Object>, Object> backup) {
TransmittableThreadLocal.doExecuteCallback(false);
Iterator iterator = ((WeakHashMap)TransmittableThreadLocal.holder.get()).keySet().iterator();
while(iterator.hasNext()) {
TransmittableThreadLocal<Object> threadLocal = (TransmittableThreadLocal)iterator.next();
if (!backup.containsKey(threadLocal)) {
iterator.remove();
threadLocal.superRemove();
}
}
setTtlValuesTo(backup);
}
restoreThreadLocalValues
private static void restoreThreadLocalValues(@NonNull HashMap<ThreadLocal<Object>, Object> backup) {
Iterator var1 = backup.entrySet().iterator();
while(var1.hasNext()) {
Entry<ThreadLocal<Object>, Object> entry = (Entry)var1.next();
ThreadLocal<Object> threadLocal = (ThreadLocal)entry.getKey();
threadLocal.set(entry.getValue());
}
}
ExecutorTtlWrapper
ExecutorTtlWrapper 本质上是一个线程池的代理,在执行 execute(…) 方法的时候,会将 Runnable 任务包装成 TtlRunnable。
class ExecutorTtlWrapper implements Executor, TtlWrapper<Executor>, TtlEnhanced {
// 这个变量代表了一个线程池
private final Executor executor;
// 这个变量是一个幂等标识符
protected final boolean idempotent;
ExecutorTtlWrapper(@NonNull Executor executor, boolean idempotent) {
this.executor = executor;
this.idempotent = idempotent;
}
public void execute(@NonNull Runnable command) {
this.executor.execute(TtlRunnable.get(command, false, this.idempotent));
}
@NonNull
public Executor unwrap() {
return this.executor;
}
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o != null && this.getClass() == o.getClass()) {
ExecutorTtlWrapper that = (ExecutorTtlWrapper)o;
return this.executor.equals(that.executor);
} else {
return false;
}
}
public int hashCode() {
return this.executor.hashCode();
}
public String toString() {
return this.getClass().getName() + " - " + this.executor.toString();
}
}
TtlEnhanced
// 这是一个单纯的空接口,用来标识一个类
public interface TtlEnhanced {
}
TtlWrapper
// TtlWrapper 用来标识一个包装类
// 需要实现获取被包装对象的 unwrap 方法
public interface TtlWrapper<T> extends TtlEnhanced {
@NonNull
T unwrap();
}
TtlExecutors
@Nullable
public static ExecutorService getTtlExecutorService(@Nullable ExecutorService executorService) {
// 如果已经包装过了,那么此处直接返回
// 如果没有包装过,那么此处包装一下
// 幂等标识符,此处默认为 true
return (ExecutorService)(!TtlAgent.isTtlAgentLoaded() && executorService != null && !(executorService instanceof TtlEnhanced) ? new ExecutorServiceTtlWrapper(executorService, true) : executorService);
}
TtlRunnable
属性与构造方法
// 本质上这是当前线程所存储的 TransmittableThreadLocal 和 ThreadLocal 的快照
private final AtomicReference<Object> capturedRef = new AtomicReference(Transmitter.capture());
private final Runnable runnable;
private final boolean releaseTtlValueReferenceAfterRun;
private final TtlAttachmentsDelegate ttlAttachment = new TtlAttachmentsDelegate();
private TtlRunnable(@NonNull Runnable runnable, boolean releaseTtlValueReferenceAfterRun) {
// 真实的业务逻辑
this.runnable = runnable;
// 当前 TtlRunnable 是否可以重复执行
// true 的情况下,只要执行完,就不能重复执行了
this.releaseTtlValueReferenceAfterRun = releaseTtlValueReferenceAfterRun;
}
get方法源码
TtlRunnable.get(…) 是一个静态方法,用于创建一个 TtlRunnable 对象。
@Nullable
public static TtlRunnable get(@Nullable Runnable runnable) {
return get(runnable, false, false);
}
@Nullable
public static TtlRunnable get(@Nullable Runnable runnable, boolean releaseTtlValueReferenceAfterRun) {
return get(runnable, releaseTtlValueReferenceAfterRun, false);
}
@Nullable
public static TtlRunnable get(@Nullable Runnable runnable, boolean releaseTtlValueReferenceAfterRun, boolean idempotent) {
//空判断
if (null == runnable) {
return null;
// 如果当前为幂等,则此处复用
} else if (runnable instanceof TtlEnhanced) {
if (idempotent) {
return (TtlRunnable)runnable;
} else {
throw new IllegalStateException("Already TtlRunnable!");
}
} else {
// 创建对象
return new TtlRunnable(runnable, releaseTtlValueReferenceAfterRun);
}
}
run方法
TtlRunnable.run() 是核心方法,是对业务逻辑的封装。
public void run() {
// 获取当前快照
Object captured = this.capturedRef.get();
// 有效性判断
if (captured != null && (!this.releaseTtlValueReferenceAfterRun || this.capturedRef.compareAndSet(captured, (Object)null))) {
// 用于创建一个当前线程的 ThreadLocal 的备份
Object backup = Transmitter.replay(captured);
try {
this.runnable.run();
} finally {
// 使用备份来恢复当前线程的 ThreadLocal 数据
Transmitter.restore(backup);
}
} else {
throw new IllegalStateException("TTL value reference is released after run!");
}
}