当前位置:网站首页>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
会重用,清除的机会就更难。
边栏推荐
- PTA 实验7-5 输出大写英文字母(10 分)
- MySQL执行sql语句的机制
- Open3D point cloud average point spacing evaluation
- Redis的常用数据结构和底层实现方式
- 【Adobe Premiere Pro 2020】pr2020安装和基本操作【PR安装、新建项目流程、导入及管理素材项目文件、添加标记、创建出入点剪辑视频、快速剪接及自动音乐卡点的方法
- WPF 实现带蒙版的 MessageBox 消息提示框
- 专业人士使用的 11 种渗透测试工具
- LeetCode_单调栈_中等_456.132 模式
- 全面了解什么是TPS、QPS以及两者的区别
- 元宇宙:下一代互联网启程(附元宇宙深度报告PDF)
猜你喜欢
C# Get system installed .NET version
Senior told me that the giant MySQL is through SSH connection
web course design
proto3-2语法
Chinese valentine's day?Programmers don't exist
x86 exception handling and interrupt mechanism (2) interrupt vector table
win10右键文件,一直转圈
程序员的专属浪漫——用3D Engine 5分钟实现烟花绽放效果
Semaphore SIGCHLD use, how to make the parent that the child performs over, how to make the distinction between multiple child processes. The end
Shell正则表达式,三剑客之grep命令
随机推荐
LeetCode_单调栈_中等_456.132 模式
Notepad++安装插件
wpf path xaml写法和c#写法对比
字符串 | 反转字符串 | 双指针法 | leecode刷题笔记
【面试高频题】可逐步优化的链表高频题
PAT1010
ZOJ 1729 & ZOJ 2006(最小表示法模板题)
LeetCode #101. 对称二叉树
虚拟机安装出现的问题汇总
Experiment record: the process of building a network
二重指针-char **、int **的作用
LeetCode 1413.逐步求和得到正数的最小值
父类的main方法可以被继承么?有什么特殊?
win10 outlook邮件设置
"Digital Economy Panorama White Paper" Special Analysis of Banking Industry Intelligent Marketing Application Released
Oracle Database Architecture
Recommend a free 50-hour AI computing platform
es6的async函数
微信支付开发流程
PAT1005