ThreadLocal的简单理解

本文深入探讨了ThreadLocal的工作原理,它如何实现线程间的数据隔离,以及与Synchronized的区别。此外,还分析了ThreadLocal、Thread和ThreadLocalMap三者的关系,并详细解释了ThreadLocal可能导致的内存泄漏问题,包括强引用和弱引用的场景。最后,讨论了为何ThreadLocalMap的key选择使用弱引用以减少内存泄漏的风险,并提出了避免内存泄漏的建议。

目录

ThreadLocal简介

ThreadLocal适用场景

ThreadLocal与Synchronized区别

Thread、ThreadLocal和ThreadLocalMap之间的关系

如何理解ThreadLocal内存泄漏

如果ThreadLocalMap的key是强引用ThreadLocal的

如果ThreadLocalMap的key是弱引用ThreadLocal的

为什么最终 key 要用弱引用呢


ThreadLocal简介

ThreadLocal中文名叫线程局部变量,意思是该变量对其他线程而言是隔离的,也就是说该变量是当前线程独有的变量。这是因为:ThreadLocal为变量在每个线程中都创建了一个副本,所以每个线程都是在访问自己内部的这个副本变量。

ThreadLocal变量,同一个ThreadLocal所包含的对象(被ThreadLocal所修饰的变量),在不同的Thread中有不同的副本。

注意:

1.每个Thread内都有自己的实例副本,且该副本只能由当前Thread使用。

2.不存在多线程共享问题。因为每个线程都有自己的实例副本,且其它线程是不能访问的。

ThreadLocal提供了线程本地的实例,它与普通变量的区别在于:每个使用该变量的线程都会初始化一个完全独立的实例副本。当一个线程结束时,它所使用的所有ThreadLocal相对应的实例副本都可以被回收。

ThreadLocal适用场景

ThreadLocal适用场景:ThreadLocal适用于每个线程需要自己独立的实例且该实例需要在多个方法中被使用,也即变量在线程间隔离而在方法或类间共享的场景。

ThreadLocal与Synchronized区别

两者都是用于解决多线程并发访问问题。

区别:

1、Synchronized用于实现线程间的数据共享,而ThreadLocal则用于实现线程间的数据隔离。

2、Synchronized是利用锁的机制,使变量或代码块中同一时刻只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时刻访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。

Thread、ThreadLocal和ThreadLocalMap之间的关系

 Thread、THreadLocal、ThreadLocalMap之间的关系如下图:

 

从这个图中我们可以非常直观的看出,ThreadLocalMap其实是Thread线程的一个属性值,而ThreadLocal是维护ThreadLocalMap这个属性指的一个工具类。Thread线程可以拥有多个ThreadLocal维护的自己线程独享的共享变量(这个共享变量只是针对自己线程里面共享)
 

如何理解ThreadLocal内存泄漏

如果ThreadLocalMap的key是强引用ThreadLocal的

假设ThreadLocalMap中的key使用了强引用, 那么会出现内存泄漏吗?

内存的引用关系如上所示。

1)假设在业务代码中使用完ThreadLocal, ThreadLocal ref被回收了
2)但是因为threadLocalMap的Entry强引用了threadLocal(key就是threadLocal), 造成ThreadLocal无法被回收

3)但是在没有手动删除Entry以及CurrentThread(当前线程)依然运行的前提下, 始终有强引用链CurrentThread Ref → CurrentThread →Map(ThreadLocalMap)-> Entry, Entry就不会被回收( Entry中包括了ThreadLocal实例和value), 导致Entry内存泄漏

也就是说 ThreadLocalMap中的key使用了强引用, 是无法完全避免内存泄漏的

如果ThreadLocalMap的key是弱引用ThreadLocal的

假设ThreadLocalMap中的key使用了弱引用, 那么会出现内存泄漏吗?

1)假设在业务代码中使用完ThreadLocal, ThreadLocal ref被回收了
2)由于threadLocalMap只持有ThreadLocal的弱引用, 没有任何强引用指向ThreadLocal实例(这里Entry不再强引用ThreadLocal了), 所以ThreadLocal就可以顺利被gc回收, 此时Entry中的key = null
3)在没有手动删除Entry以及CurrentThread依然运行的前提下, 也存在始终有强引用链CurrentThread Ref → CurrentThread →Map(ThreadLocalMap)-> Entry,value就不会被回收, 而这块value永远不会被访问到了(因为key=null), 导致value内存泄漏
也就是说: ThreadLocalMap中的key使用了弱引用, 也有可能内存泄漏

为什么最终 key 要用弱引用呢

上面已经分析过了,无论 ThreadLocalMap 中的 key 使用哪种类型引用都无法完全避免内存泄漏,那为什么要使用弱引用呢

通过分析可以知道,要避免内存泄漏有两种方式:

  • ​ 1 .使用完 ThreadLocal ,调用其 remove 方法删除对应的 Entry
  • ​ 2 .使用完 ThreadLocal ,当前 Thread 也随之运行结束

相对第一种方式,第二种方式显然更不好控制,特别是使用线程池的时候,线程结束是不会销毁的.

也就是说,只要记得在使用完ThreadLocal 及时的调用 remove ,无论 key 是强引用还是弱引用都不会有问题.

那么为什么 key 要用弱引用呢

事实上,在 ThreadLocalMap 中的set/getEntry 方法中,会对 key 为 null(也即是 ThreadLocal 为 null )进行判断,如果为 null 的话,那么会把 value 置为 null 的.

​ 这就意味着使用完 ThreadLocal , CurrentThread 依然运行的前提下.就算忘记调用 remove 方法,弱引用比强引用可以多一层保障:弱引用的 ThreadLocal 会被回收.对应value在下一次 ThreadLocaIMap 调用 set/get/remove 中的任一方法的时候会被清除,从而避免内存泄漏.
 

### ThreadLocal 概念 `ThreadLocal` 是一种特殊的变量,在每个线程中都有独立的副本,这些副本仅限于当前线程访问和修改。由于这种隔离特性,不同线程之间不会相互影响,从而解决了多线程环境下的数据共享问题[^1]。 #### 创建与使用 `ThreadLocal` 下面是一个简单的例子展示如何定义并操作 `ThreadLocal` 变量: ```java public class Example { private static final ThreadLocal<Integer> threadLocalValue = new ThreadLocal<>(); public void set(int value) { threadLocalValue.set(value); } public Integer get() { return threadLocalValue.get(); } } ``` 在这个案例里,每当有新的线程执行到这段代码时都会为自己分配一个新的整数类型的存储空间用于保存特定的数据值。 ### 面试常见问题 对于理解 `ThreadLocal` 的工作机制以及应用场景非常重要,以下是几个可能遇到的相关面试题目: 1. **为什么需要 `ThreadLocal`?** - 主要是为了实现线程安全的操作而设计的一种解决方案。通过给每一个线程提供自己独有的实例化对象或基本类型成员变量的方式避免了多个线程竞争同一个资源所带来的同步开销。 2. **`ThreadLocal` 是否会造成内存泄漏?** - 如果不及时清理不再使用的 `ThreadLocal` 实例,则可能导致垃圾回收器无法释放关联的对象引用链路所指向的内容,进而引发潜在的风险。因此建议在线程结束前清除掉不必要的 `ThreadLocal` 数据以防止这种情况发生。 3. **`ThreadLocal` 和锁机制有什么区别?** - 锁机制主要是为了控制对公共资源(比如文件句柄、数据库连接等)的安全并发访问;相比之下,`ThreadLocal` 则更侧重于为每个单独的工作单元——也就是各个不同的线程维护各自的状态信息而不必担心与其他线程之间的干扰。 4. **什么时候应该考虑使用 `ThreadLocal`?** - 当应用程序中有许多短生命周期的任务并且希望减少因频繁创建销毁临时对象带来的性能损耗时可以选择采用这种方式来进行优化处理。另外在某些情况下也可以利用它来传递上下文参数,例如事务传播级别或者用户身份认证令牌之类的信息贯穿整个业务流程之中。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值