当前位置:网站首页>三种ThreadLocal,玩转线程变量保存与传递
三种ThreadLocal,玩转线程变量保存与传递
2022-08-09 13:15:00 【软件开发随心记】
ThreadLocal
ThreadLocal是jdk中自带的类,用于保存本线程专有的数据,从下面的代码大家可以很直接的看到它的作用
private static ThreadLocal<String> sThreadLocal=new ThreadLocal<>();
@Test
void testThreadlocal() {
//主线程
sThreadLocal.set("这是在主线程中");
System.out.println("线程名字:"+Thread.currentThread().getName()+"---"+sThreadLocal.get());
//线程a
new Thread(new Runnable() {
public void run() {
sThreadLocal.set("这是在线程a中");
System.out.println("线程名字:"+Thread.currentThread().getName()+"---"+sThreadLocal.get());
}
},"线程a").start();
//线程b
new Thread(new Runnable() {
public void run() {
sThreadLocal.set("这是在线程b中");
System.out.println("线程名字:"+Thread.currentThread().getName()+"---"+sThreadLocal.get());
}
},"线程b").start();
}
运行结果:

很明显可以看到,sThreadLocal变量只有一份,为字符串类型,但是主线程和ab两个子线程打印出来的sThreadLocal变量值是不一样的,这就是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的变量,与Hashmap类似,我们的value就是设置到这里面的,而再次点进去发现getMap方法是从Thread类本身去拿的,那么为什么ThreadLocal变量和线程绑定也就很好理解了,因为保存ThreadLocal的ThreadLocalMap就是线程的一个变量。
Thread.class:
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocal一般配合tomcat的一个请求一个业务线程的模型使用,保存类似于用户信息等全局都可能用到的内容,避免变量透传。
InheritableThreadLocal
看名字就知道,这个类是用于父子线程的变量传递,我们只需在上面测试代码上略作修改
private static InheritableThreadLocal<String> sThreadLocal=new InheritableThreadLocal<>();
@Test
void testThreadlocal() {
//主线程
sThreadLocal.set("这是在主线程中");
System.out.println("线程名字:"+Thread.currentThread().getName()+"---"+sThreadLocal.get());
//线程a
new Thread(new Runnable() {
public void run() {
System.out.println("线程名字:"+Thread.currentThread().getName()+"---"+sThreadLocal.get());
}
},"线程a").start();
}
运行结果:
可以看到我们新建线程后明明没有任何set sThreadLocal的行为,但依然能获取到主线程(父线程)的sThreadLocal变量,这就是InheritableThreadLocal和 ThreadLocal区别所在,变量可以由父线程传给子线程。
TransmittableThreadLocal
实际开发中InheritableThreadLocal一般很少用到,原因很简单,正式的开发中没人会直接new一个线程,这样会导致线程难以管理和回收,绝大多数项目在涉及到多线程的时候都会使用线程池,但是这也带来一个问题,线程池中的线程和业务主线程没有任何从属关系,那是不是ThreadLocal的变量只能显式透传过去了呢,这时候我们可以使用阿里开源的组件transmittable-thread-local 解决。
//直接使用包装好的线程池即可
static ExecutorService pool = TtlExecutors.getTtlExecutorService(Executors.newFixedThreadPool(3));
如果是使用spring异步注解@Aynsc也是同理,只需自定义异步线程池为ttlExecutor即可
TransmittableThreadLocal原理也很简单,关键在于自定义的TtlRunnable完成了转存ThreadLocal动作
@Override
public void run() {
final Object captured = capturedRef.get();
if (captured == null || releaseTtlValueReferenceAfterRun && !capturedRef.compareAndSet(captured, null)) {
throw new IllegalStateException("TTL value reference is released after run!");
}
//这一步就是转存变量动作
final Object backup = replay(captured);
try {
runnable.run();
} finally {
restore(backup);
}
}
//实际执行转存方法
private static HashMap<ThreadLocal<Object>, Object> replayThreadLocalValues(@NonNull HashMap<ThreadLocal<Object>, Object> captured) {
final HashMap<ThreadLocal<Object>, Object> backup = new HashMap<ThreadLocal<Object>, Object>();
for (Map.Entry<ThreadLocal<Object>, Object> entry : captured.entrySet()) {
final ThreadLocal<Object> threadLocal = entry.getKey();
backup.put(threadLocal, threadLocal.get());
final Object value = entry.getValue();
if (value == threadLocalClearMark) threadLocal.remove();
else threadLocal.set(value);
}
return backup;
}
边栏推荐
猜你喜欢

问题系列-如何修改或更新localhost里的值

一篇ngork直接使用

微服务+微信小程序实现社区服务

Jetpack Compose——Button(按钮)的使用

FFmpeg multimedia file processing (implementation of ffmpeg operation directory and list)

海康设备获取YV12图像-不用rtsp

error Trailing spaces not allowed no-trailing-spaces 9:14 error Unexpected trailing comma

企业公众号开通微信支付

蓝桥历届真题-既约分数

禁止输入(×),按键精灵小程序,快速上手
随机推荐
二叉树的遍历(py)
RTP打包发送H.264
tensorflow图片编码处理基础
ArcEngine(九)图形绘制
Q_04_04 Q#类型模型
IDEA Gradle 常遇问题(一)
openharmony容器组件之Badge
eslint语法规则报错
javscript基础易错点集合
Q_04_07 进一步探索
分布式系统关注点(8)——99%的人都能看懂的「熔断」以及最佳实践 (转载非原创)
神经网络与深度学习(TensorFlow)
Professor Chen Qiang the machine learning and R application course chapter 18 assignments
NC192 二叉树的后序遍历
对百度的内容进行修改
微服务+微信小程序实现社区服务
Q_06_03 表达式
FFmpeg长时间无响应的解决方法
Jetpack Compose——remember、mutableStateOf、rememberSaveable
Jetpack Compose——TextField及OutlinedTextField(文本框)的使用