当前位置:网站首页>ThreadLocal的简单理解
ThreadLocal的简单理解
2022-07-03 06:03:00 【xuhss_com】
优质资源分享
| 学习路线指引(点击解锁) | 知识定位 | 人群定位 |
|---|---|---|
| 🧡 Python实战微信订餐小程序 🧡 | 进阶级 | 本课程是python flask+微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一个全栈订餐系统。 |
| Python量化交易实战 | 入门级 | 手把手带你打造一个易扩展、更安全、效率更高的量化交易系统 |
目录* 一、背景
- 二、ThreadLocal解决的问题
- 三、如何创建一个ThreadLocal实例
- 四、ThreadLocal如何做到线程变量隔离
- 五、ThreadLocalMap中的hash冲突是如何处理的
- 六、ThreadLocal内存泄漏
一、背景
最近有人问我ThreadLocal是如何做到在每个线程中的值都是隔离的,此处写篇文章来简单记录下。
二、ThreadLocal解决的问题
- 该数据属于该线程
Thread自身,别的线程无法对其影响。(需要注意:需要调用ThreadLocal的remove方法) - 不存在线程安全问题。(因为
ThreadLocal类型的变量只有自身的线程可以访问,所以这点是成立的。)
比如:
用户登录成功后,需要将登录用户信息保存起来,以方便在系统中的任何地方都可以使用到,那么此时就可以使用ThreadLocal来实现。例如:Spring Security中的ThreadLocalSecurityContextHolderStrategy类。
三、如何创建一个ThreadLocal实例
private static final ThreadLocal USER\_NAME = new ThreadLocal<>();
ThreadLocal的实例推荐使用private static final来修饰。
四、ThreadLocal如何做到线程变量隔离
1、理解3个类
ThreadLocal: 此类提供了一个简单的set,get,remove方法,用于设置,获取或移除 绑定到线程本地变量中的值。ThreadLocalMap: 这是在ThreadLocal中定义的一个类,可以简单的将它理解成一个Map,不过它的key是WeakReference弱引用类型,这样当这个值没有在别的地方引用时,在发生垃圾回收时,这个map的key会被自动回收,不过它的值不会被自动回收。
static class Entry extends WeakReference> {
Object value;
Entry(ThreadLocal k, Object v) {
// key 弱引用
super(k);
// 值强引用
value = v;
}
}
Thread:这个是线程类,在这个类中存在一个threadLocals变量,具体的类型是ThreadLocal.ThreadLocalMap。
2、看下set方法是如何实现的
public void set(T value) {
// 获取当前线程
Thread t = Thread.currentThread();
// 获取绑定到这个线程自身的 ThreadLocalMap,这个ThreadLocalMap是从Thread类的`threadLocals`变量中获取的
ThreadLocalMap map = getMap(t);
if (map != null) {
// 向map中设置值,key为 ThreadLocal 对象的实例。
map.set(this, value);
} else {
// 如果map不存在,则创建出来。
createMap(t, value);
}
}
通过上方的代码,我们可知: 当我们向ThreadLocal中设置一个值,会经过如下几个步骤:
- 获取当前线程
Thread - 获取当前线程的
ThreadLocalMap对象。 - 向
ThreadLocalMap中设置值,key为ThreadLocal对象,值为具体的值。
3、看看 get 方法如何实现
public T get() {
// 获取当前线程
Thread t = Thread.currentThread();
// 获取这个线程自身绑定的 ThreadLocalMap 对象
ThreadLocalMap map = getMap(t);
if (map != null) {
// this是ThreadLocal对象,获取Map中的Entry对象
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
// 获取具体的值
T result = (T)e.value;
return result;
}
}
// 设置初始值
return setInitialValue();
}
从上方的get 和 set 方法可以得知,通过往ThreadLocal对象中设置值或获取值,其实是最终操作到Thread对象中的threadLocals字段中,而这个字段是Thread自身的,因此做到了隔离。
五、ThreadLocalMap中的hash冲突是如何处理的
1、ThreadLocal对象的hash值是怎样的
private final int threadLocalHashCode = nextHashCode();
// 该 ThreadLocal 对象自身的hash code值
private final int threadLocalHashCode = nextHashCode();
// 从0开始
private static AtomicInteger nextHashCode = new AtomicInteger();
// 每次递增固定的值
private static final int HASH\_INCREMENT = 0x61c88647;
// hash code 值计算
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
从上方的代码中可以,ThreadLocal类在实例化出来之后,它的hash code值(threadLocalHashCode)就是固定的,即使ThreadLocal调用了set方法,设置了别的值,它的hash code值也不会发生变化。
此字段threadLocalHashCode为ThreadLocal对象的hash值,在ThreadLocalMap中需要用到这个hash值。
2、解决hash冲突

ThreadLocalMap解决hash冲突的办法很简单。就是通过线性探测法。如果发生了冲突,就去找数组后面的可用位置。具体看上图。演示的是A和B 2个ThreadLocal对象,然后发生了冲突,A和B存在的位置在那个地方。
六、ThreadLocal内存泄漏
ThreadLocal为什么会存在内存泄漏呢?
这是因为ThreadLocalMap中的key是WeakReference类型,也就是弱引用类型,而弱引用类型的数据在没有外部强引用类型的话,在发生gc的时候,会自动被回收掉。注意: 此时是key被回收了,但是value是没有回收的。因此在ThreadLocalMap中的Entry[]中可能存在key是null,但是value是具体的值的对象,因此就发生了内存泄漏。
解决内存泄漏:
当我们使用完ThreadLocal对象后,需要在适当的时机调用ThreadLocal#remove()方法。 否则就只有等Thread自动退出才能清除,如果是使用了线程池,Thread会重用,清除的机会就更难。
边栏推荐
- AtCoder Beginner Contest 258(A-D)
- CAD插件的安装和自动加载dll、arx
- pytorch 多分类中的损失函数
- Detailed explanation of iptables (1): iptables concept
- Crontab command usage
- Kubernetes resource object introduction and common commands (V) - (configmap)
- Ansible firewall firewalld setting
- 卷积神经网络CNN中的卷积操作详解
- How to create your own repository for software packages on Debian
- 多线程与高并发(7)——从ReentrantLock到AQS源码(两万字大章,一篇理解AQS)
猜你喜欢

Jedis source code analysis (I): jedis introduction, jedis module source code analysis

SVN分支管理

phpstudy设置项目可以由局域网的其他电脑可以访问

卷积神经网络CNN中的卷积操作详解

Convolution operation in convolution neural network CNN

Detailed explanation of contextclassloader

Apache+php+mysql environment construction is super detailed!!!

项目总结--04

从小数据量分库分表 MySQL 合并迁移数据到 TiDB

Introduction to redis using Lua script
随机推荐
Oauth2.0 - Introduction and use and explanation of authorization code mode
为什么网站打开速度慢?
[advanced pointer (1)] | detailed explanation of character pointer, pointer array, array pointer
从小数据量分库分表 MySQL 合并迁移数据到 TiDB
Kubernetes notes (10) kubernetes Monitoring & debugging
[advanced pointer (2)] | [function pointer, function pointer array, callback function] key analysis + code explanation
[branch and cycle] | | super long detailed explanation + code analysis + a trick game
[teacher Zhao Yuqiang] MySQL flashback
How does win7 solve the problem that telnet is not an internal or external command
AtCoder Beginner Contest 258(A-D)
Oauth2.0 - explanation of simplified mode, password mode and client mode
Kubernetes notes (VIII) kubernetes security
Understand expectations (mean / estimate) and variances
Detailed explanation of findloadedclass
Kubernetes cluster environment construction & Deployment dashboard
Kubernetes notes (VII) kuberetes scheduling
Detailed explanation of contextclassloader
Together, Shangshui Shuo series] day 9
Ensemble, série shuishu] jour 9
Strategy pattern: encapsulate changes and respond flexibly to changes in requirements