TransmittableThreadLocal 四千字超详细讲解

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 则会影响后续逻辑

回到正题 TransmittableThreadLocal

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值