TransmittableThreadLocal
ThreadLocal 前情提要
ThreadLocal (后续简称TL) 为本地线程遍历,用于实现线程之间的变量隔离
实现原理
Thead 线程类 持有ThreadMap 集合,key为ThreadLocal 引用,value 为ThreadLocal 调用Set 时设置的Value,通过ThreadLocal 的引用 就可以在当前线程 Thread 类 中的ThreadMap 拿到 对应的Value
源码细节
翻看Thead 类 中你会发现 ThreadLocal.ThreadLocalMap threadLocals = null; 同时在Thread.init() 方法中并没有对threadLocals 进行赋值,这是jdk惯用的懒加载,当你的线程第一次调用了 ThreadLoacl.set() 方法时会触发 ThreadLocal.ThreadLocalMap 的初始化
java
体验AI代码助手
代码解读
复制代码
void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }
具体初始化方法,没有特别需要注意的
java
体验AI代码助手
代码解读
复制代码
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) { table = new Entry[INITIAL_CAPACITY]; int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); table[i] = new Entry(firstKey, firstValue); size = 1; setThreshold(INITIAL_CAPACITY); }
因为本身集合的维度就是线程维度的,所以不需要考虑并发安全
InheritableThreadLocal 提要
在讲解 TransmittableThreadLocal (后续简称TTL) 还需要了解这个InheritableThreadLocal (后续简称ITL)类 因为TTL很多的功能实际是继承ITL获得的
InheritableThreadLocal 是JDK 为了解决Tl 在 父子线程的传值问题。
实现原理
重写了TL的 getMap 与 createMap 方法,使其调用线程的 inheritableThreadLocals
java
体验AI代码助手
代码解读
复制代码
ThreadLocalMap getMap(Thread t) { return t.inheritableThreadLocals; } void createMap(Thread t, T firstValue) { t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue); }
而 inheritableThreadLocals 则是在 Thread.init() 方法时从主线程进行赋值构造
java
体验AI代码助手
代码解读
复制代码
if (inheritThreadLocals && parent.inheritableThreadLocals != null) this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
createInheritedMap 方法
java
体验AI代码助手
代码解读
复制代码
private 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 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 c = new Entry(key, value); int h = key.threadLocalHashCode & (len - 1); while (table[h] != null) h = nextIndex(h, len); table[h] = c; size++; } } } }
需要注意的是ITL并不能然你选择使用深拷贝而只能使用浅拷贝
**测试代码 **
java
体验AI代码助手
代码解读
复制代码
static class Student{ String name; int age; public Student() { this.name = "zhangsan"; this.age = 18; } } public static void main(String[] args) { InheritableThreadLocal<Student> threadLocal = new InheritableThreadLocal<>(); threadLocal.set(new Student()); new Thread(()->{ // 父子线程TL值传递 Student student = threadLocal.get(); System.out.println(student); // 修改引用对象值 student.name = "lisi"; student.age = 20; }).start(); try { Thread.sleep(500); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println(threadLocal.get()); }
结果
ini
体验AI代码助手
代码解读
复制代码
Student{name='zhangsan', age=18} Student{name='lisi', age=20}
总结
ITL 只是简单的实现了TLMap的线程上下文的赋值传递,但距离实际项目中使用还差很多功能,
-
比如池化线程复用时的TL必须要在每次任务末尾进行情况否则下次任务会出现上次任务的TL值,
-
部分业务场景需要引用对象应该遵循单向传递(就像 Vue 的父子组件的props传递那样)
-
如果线程池触发了
CallerRunsPolicy拒绝策略时由调用线程执行了任务末尾的ITL.remove 则会影响后续逻辑

最低0.47元/天 解锁文章
5784

被折叠的 条评论
为什么被折叠?



