ThreadLocal介绍

什么是ThreadLocal?
        ThreadLocal类顾名思义可以理解为线程本地变量。也就是说如果定义了一个ThreadLocal,每个线程往这个ThreadLocal中读写是线程隔离,互相之间不会影响的。它提供了一种将可变数据通过每个线程有自己的独立副本从而实现线程封闭的机制。

它大致的实现思路是怎样的?
        Thread类有一个类型为ThreadLocal.ThreadLocalMap的实例变量threadLocals,也就是说每个线程有一个自己的ThreadLocalMap。ThreadLocalMap有自己的独立实现,可以简单地将它的key视作ThreadLocal,value为代码中放入的值(实际上key并不是ThreadLocal本身,而是它的一个弱引用)。每个线程在往某个ThreadLocal里塞值的时候,都会往自己的ThreadLocalMap里存,读也是以某个ThreadLocal作为引用,在自己的map里找对应的key,从而实现了线程隔离。
        ThreadLocal 这个类提供线程局部变量,这些变量与其他正常的变量的不同之处在于,每一个访问该变量的线程在其内部都有一个独立的初始化的变量副本;ThreadLocal 实例变量通常采用private static 在类中修饰。
        只要 ThreadLocal 的变量能被访问,并且线程存活,那每个线程都会持有 ThreadLocal 变量的副本。当一个线程结束时,它所持有的所有 ThreadLocal 相对的实例副本都可被回收。
        一句话说就是 ThreadLocal 适用于每个线程需要自己独立的实例且该实例需要在多个方法中被使用(相同线程数据共享),也就是变量在线程间隔离(不同的线程数据隔离)而在方法或类间共享的场景。

注意事项
        因为线程的创建比较昂贵,所以 Web 服务器往往会使用线程池来处理请求,这就意味着线程会被重用。这时,使用类似 ThreadLocal 工具来存放一些数据时,需要特别注意在代码运行完后,显式地去清空设置的数据。如果在代码中使用了自定义的线程池,也同样会遇到这个问题。

        线程复用会产生脏数据。由于线程 池会重用Thread对象,那么与Thread绑定的类的静态属性ThreadLocal变量也会被重用。如果在实现的线程run()方法体中不显式地调用于线程相关的ThreadLocal信息,那么倘若下一个线程不调用set()设置初始值,就可能get到重用的线程信息,包括ThreadLocal所关联的线程对象的value值。


        ThreadLocal 在 ThreadLocalMap 中是以一个弱引用身份被Entry中的Key引用的,因此如果ThreadLocal没有外部强引用来引用它,那么ThreadLocal会在下次JVM垃圾收集时被回收。这个时候就会出现Entry中Key已经被回收,出现一个null Key的情况,外部读取ThreadLocalMap中的元素是无法通过null Key来找到Value的。因此如果当前线程的生命周期很长,一直存在,那么其内部的ThreadLocalMap对象也一直生存下来,这些null key就存在一条强引用链的关系一直存在:Thread --> ThreadLocalMap-->Entry-->Value,这条强引用链会导致Entry不会回收,Value也不会回收,但Entry中的Key却已经被回收的情况,造成内存泄漏。


        JVM做了一些措施来保证ThreadLocal尽量不会内存泄漏:在ThreadLocal的get()、set()、remove()方法调用的时候会清除掉线程ThreadLocalMap中所有Entry中Key为null的Value,并将整个Entry设置为null,利于下次储存

        由于ThreadLocalMap的key是弱引用,而Value是强引用。这就导致了一个问题,ThreadLocal在没有外部对象强引用时,发生GC时弱引用Key会被回收,而Value不会回收,如果创建ThreadLocal的线程一直持续运行,那么这个Entry对象中的value就有可能一直得不到回收,发生内存泄露。


为什么ThreadLocalMap的key是弱引用呢?
key 使用强引用:引用的ThreadLocal的对象被回收了,但是ThreadLocalMap还持有ThreadLocal的强引用,如果没有手动删除,ThreadLocal不会被回收,导致Entry内存泄漏。

key 使用弱引用:引用的ThreadLocal的对象被回收了,由于ThreadLocalMap持有ThreadLocal的弱引用,即使没有手动删除,ThreadLocal也会被回收。value在下一次ThreadLocalMap调用set,get,remove的时候会被清除。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`ThreadLocal`是Java中提供的一种线程局部变量(Thread Local Variable)机制,它为每个线程创建了一个独立的副本,因此在多线程环境下,每个线程都有自己的变量值,不会相互干扰。`ThreadLocal`的主要作用是在不使用synchronized关键字的情况下实现线程间的局部数据隔离。 以下是`ThreadLocal`的主要特点和用法: 1. **自动绑定**:当在一个线程中对`ThreadLocal`变量赋值时,这个值会自动绑定到当前线程上,其他线程无法访问该值,除非它们也显式地为这个变量赋值。 2. **线程安全**:`ThreadLocal`本身并不保证线程安全,因为它不提供同步机制。如果需要在多个线程之间共享某个值,需要开发者自己管理同步。 3. **清除值**:每个线程结束时,其关联的`ThreadLocal`变量会被自动清零,如果需要手动清除,可以调用`ThreadLocal.remove()`方法。 4. **静态方法访问**:`ThreadLocal`提供了一个静态方法`get()`, 用于获取当前线程的副本值,如果没有为该线程设置过值,则返回`initialValue`参数的默认值。 ```java ThreadLocal<String> threadLocal = new ThreadLocal<>(); // 在一个线程中设置并读取值 threadLocal.set("Hello"); String value = threadLocal.get(); // 在当前线程中,value等于"Hello" // 在另一个线程中,值是空的或默认值 threadLocal.set("World"); value = threadLocal.get(); // 在这个线程中,value等于"World" ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值