当前位置:网站首页>Simple understanding of ThreadLocal
Simple understanding of ThreadLocal
2022-08-09 12:54:00 【head wig】
目录
- 二、ThreadLocal解决的问题
- 三、如何创建一个ThreadLocal实例
- 四、ThreadLocal如何做到线程变量隔离
- 2、看下set方法是如何实现的
- 3、看看 get 方法如何实现
- 五、ThreadLocalMap中的hash冲突是如何处理的
- 1、ThreadLocal对象的hash值是怎样的
- 六、ThreadLocal内存泄漏
一、背景
最近有人问我 ThreadLocal 是如何做到在每个线程中的值都是隔离的,此处写篇文章来简单记录下.
二、ThreadLocal解决的问题
- 该数据属于该线程
Thread自身,别的线程无法对其影响. (需要注意:需要调用ThreadLocal的remove方法) - 不存在线程安全问题. (因为
ThreadLocal类型的变量只有自身的线程可以访问,所以这点是成立的.)
比如:
用户登录成功后,需要将 登录用户信息 保存起来,以方便在系统中的任何地方都可以使用到,那么此时就可以使用 ThreadLocal 来实现.例如: Spring Security 中的 ThreadLocalSecurityContextHolderStrategy 类.
三、如何创建一个ThreadLocal实例
private static final ThreadLocal<String> 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<ThreadLocal<?>> { 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 会重用,清除的机会就更难.
边栏推荐
- LeetCode 1413.逐步求和得到正数的最小值
- CANopen DS402名词
- JD.com architects tidy up: what are the core technical knowledge points of jvm and performance tuning
- 金融业“限薪令”出台/ 软银出售过半阿里持仓/ DeepMind新实验室成立... 今日更多新鲜事在此...
- 00后写个暑假作业,被监控成这笔样
- 获取url地址中问号后参数(即使是iframe也可以)
- ARP协议原理
- HAproxy: load balancing
- 信息系统项目管理师必背核心考点(六十三)项目组合管理的主要过程&DIPP分析
- matlab simulink的scope 示波器光标如何移动记录
猜你喜欢

Information system project managers must memorize the core test sites (63) The main process of project portfolio management & DIPP analysis

字符串 | 反转字符串 | 双指针法 | leecode刷题笔记
字节秋招二面把我干懵了,问我SYN报文什么情况下会被丢弃?

The grep command Shell regular expressions, the three musketeers

曲鸟全栈UI自动化教学(八):框架代码讲解和进一步优化
Byte Qiu Zhao confused me on both sides, and asked me under what circumstances would the SYN message be discarded?

二重指针-char **、int **的作用

ARP协议原理

900页数学论文证明旋转的黑洞不会爆炸,丘成桐:30多年来广义相对论首次重大突破...

专业人士使用的 11 种渗透测试工具
随机推荐
你没见过的《老友记》镜头,AI给补出来了|ECCV 2022
IDEA 关闭/开启引用提示Usages
放下手机吧:实验表明花20分钟思考和上网冲浪同样快乐
Two ways to enter the Oracle database
父类的main方法可以被继承么?有什么特殊?
How should the acceptance criteria for R&D requirements be written?| Agile Practices
LeetCode热题(11.合并两个有序链表)
The redis library cannot be imported
We really need DApp?Really can't meet our fantasy App?
Apexsqlrecover无法连接数据库
信息系统项目管理师必背核心考点(六十三)项目组合管理的主要过程&DIPP分析
JS 封装节流(后期优化)
web course design
F280049库函数API编程、直接寄存器控制编程和混合编程方法
MySQL事务隔离级别
Visual Studio 2017 ASP.NET Framework MVC 项目 MySQL 配置连接
腾讯欲成育碧最大股东/ 米哈游招NLP内容生成研究员/ AI发现四千余物种濒临灭绝...今日更多新鲜事在此...
HAproxy: load balancing
【无标题】
proto3-2语法