【Java】浅谈ThreadLocal

相信大家对ThreadLocal都不陌生吧,但是ThreadLocal是什么呢,它又有什么作用呢,我们平常怎么使用ThreadLocal,我们一块来探讨一下。

什么是ThreadLocal

ThreadLocal是Java中的一个类,它提供了线程局部变量的支持。每个ThreadLocal对象都可以维护一个线程私有的变量副本,这个变量副本只能被当前线程访问和修改,其他线程无法直接访问到。

通俗的来说ThreadLocal就是一个线程变量,实现了线程内的资源共享和线程间的资源隔离,为每个线程提供独立的变量副本,避免线程间的数据共享,常用于解决线程安全问题,特别是在多线程共享对象时,之所以会出现线程安全问题,就是因为资源共享问题,每个 Thread 有自己的实例副本,且其它 Thread 不可访问,那就不存在多线程间共享的问题,这也是ThreadLocal的重要作用。

ThreadLocal原理

查看Thread类源码可以发现有一个threadLocals变量,它是ThreadLocal内部类ThreadLocalMap类型的变量,我们通过查看内部类ThreadLocalMap可以发现实际上它类似于一个HashMap。在默认情况下,每个线程中的这个变量都为null。

 当线程调用ThreadLocal的set方法或get方法,才会进行创建。我们还可以通过源码发现,每个线程的本地变量并不是存放在ThreadLocal实例中的,而是存储在了对应线程的 threadLocals 变量里面,其中以ThreadLocal作为key,每个线程的本地变量作为value,通过set方法将value添加到调用线程的threadLocals中。

set方法源码

 用getMap方法来获取当前线程对应的 threadLocals,如果调用getMap方法返回值不为null,就直接将value值设置到threadLocals中;如果getMap方法返回null说明是第一次调用set方法,这个时候就需要调用createMap方法创建threadLocals。

get方法源码

先获取当前调用线程,若当前调用线程的threadLocals != null,就直接返回当前线程中的本地变量值 threadLocals,否则执行setInitialValue方法来初始化threadLocals变量。在setInitialValue方法中,类似于set方法的实现。

ThreadLocal内存泄露问题

内存泄露:指程序中动态分配的堆内存由于某种原因没有被释放或者无法释放,造成系统内存的浪费,导致程序运行速度减慢或者系统奔溃等严重后果。内存泄露堆积将会导致内存溢出。

为什么会出现内存泄露问题呢?

从上面的源码我们不难发现ThreadLocalMap是Thread类的一个属性,被当前线程所引用,因此它的生命周期应当是与Thread一样长的,那么在使用完ThreadLocal后,如果对应的Thread也随之结束,ThreadLocalMap就会自然而然被gc回收,但是我们使用线程池时,Thread结束是不会被销毁的,那么就需要手动调用remove方法来避免内存泄露。

remove方法源码:

ThreadLocalMap中的Key为什么要设计为弱引用呢? 

ThreadLocalMap中的Key被设计为弱引用还是为了避免内存泄漏问题。如果ThreadLocalMap中的Key是强引用,那么当ThreadLocal实例被回收时,其对应的Key也无法被回收,这可能导致内存泄漏。通过使用弱引用作为ThreadLocalMap的Key,当ThreadLocal实例没有被线程或其他地方引用时,它就可以被垃圾回收,其对应的Entry(键值对)也会从ThreadLocalMap中自动移除。这样就避免了线程本地变量持有过期引用的问题,使得不再被活动线程使用的ThreadLocal对象能够被垃圾回收,从而防止潜在的内存泄漏。相当于为使用完ThreadLocal后没有调用remove方法上了一层保险。

ThreadLocal变量与普通变量的区别

官方解释:

This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID).

翻译过来就是:ThreadLocal提供的是一种线程局部变量。这些变量不同于其它变量的点在于每个线程在获取变量的时候,都拥有它自己相对独立的变量副本。ThreadLocal 的实例一般是私有静态的,可以做到与一个线程绑定某一种状态。

ThreadLocal作为静态的存在,在一些需要层层调用函数并传递局部变量的场景下,也是一个很好的选择。

ThreadLocal的使用场景

  1. 线程上下文信息传递:ThreadLocal可用于在线程间传递上下文信息,避免显式地传递参数。如,Web应用中可以将用户信息、请求信息等存储在ThreadLocal中,在不同层级的方法中直接访问,避免了显式传递这些信息。

  2. 线程安全的对象:如果某个对象在单线程中是不可变的,但在多线程环境下需要共享,则可以使用ThreadLocal存储该对象的副本,每个线程独立访问自己的副本,避免了线程安全问题。

  3. 数据库连接管理:在数据库操作中,使用ThreadLocal存储数据库连接可以确保每个线程都使用独立的数据库连接,避免了线程间的竞争和同步开销,同时简化了数据库连接的管理。

  4. 事务管理:在事务处理中,可以使用ThreadLocal存储事务上下文,保证每个线程独立的事务处理,避免了事务干扰和资源竞争。

  5. 全局共享对象的避免:在一些场景下,全局共享对象可能引起性能问题或线程安全问题。使用ThreadLocal可以为每个线程提供独立的对象实例,避免了全局共享对象的竞争和同步问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值