当前位置:网站首页>ThreadLocal的简单理解
ThreadLocal的简单理解
2022-08-09 11:47:00 【头顶假发】
目录
- 二、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
会重用,清除的机会就更难。
边栏推荐
猜你喜欢
Visual Studio 2017 ASP.NET Framework MVC 项目 MySQL 配置连接
wpf path xaml写法和c#写法对比
抗积分饱和 PID代码实现,matlab仿真实现
程序员的专属浪漫——用3D Engine 5分钟实现烟花绽放效果
微信一面:一致性哈希是什么,使用场景,解决了什么问题?
【面试高频题】可逐步优化的链表高频题
Shell之常用小工具(sort、uniq、tr、cut)
[Essence] Analysis of the special case of C language structure: structure pointer / basic data type pointer, pointing to other structures
C# async 和 await 理解
WPF 实现带蒙版的 MessageBox 消息提示框
随机推荐
Modify the VOT2018.json file and remove the color in the image path
香港服务器如何进行加密?
WPF implements a MessageBox message prompt box with a mask
x86 Exception Handling and Interrupt Mechanism (1) Overview of the source and handling of interrupts
ACM01 Backpack problem
湖南进芯电子替代TIC2000的可能性
专业人士使用的 11 种渗透测试工具
TIC2000调用API函数Flash擦除片上FLASH失败
PAT1003
matlab simulink的scope 示波器光标如何移动记录
【重要】C语言进阶 -- 自定义类型:结构体、枚举、联合
Information system project managers must memorize the core test sites (63) The main process of project portfolio management & DIPP analysis
mysql + redis + flask + flask-sqlalchemy + flask-session 配置及项目打包移植部署
微信一面:一致性哈希是什么,使用场景,解决了什么问题?
x86 exception handling and interrupt mechanism (2) interrupt vector table
在北京参加UI设计培训到底怎么样?
es6对象迭代器iterator
PAT1005
从零开始Blazor Server(9)--修改Layout
redis内存的淘汰机制