ThreadLocal 相关知识点

1.ThreadLocal 是什么?

        ThreadLocal 是一个本地线程副本变量工具类。主要用于将私有线程和该线程存放的副本对象做一个映射,各个线程之间的变量互不干扰,在高并发场景下,可以实现无状态的调用,适用于各个线程不共享变量值的操作。

2. ThreadLocal 工作原理是什么?

        TreadLocal 原理:每个线程的内部都维护了一个 ThreadLocalMap,它是一个 Map(key,value)数据格式,key 是一个弱引用,也就是 ThreadLocal 本身,而 value 存的是线程变量的值。也就是说 ThreadLocal 本身并不存储线程的变量值,它只是一个工具,用来维护线程内部的 Map,帮助存和取变量。数据结构,如下图所示:

                                   

3.ThreadLocal 如何解决 Hash 冲突?

       与 HashMap 不同,ThreadLocalMap 结构非常简单,没有 next 引用,也就是说 ThreadLocalMap 中解决 Hash 冲突的方式并非链表的方式,而是采用线性探测的方式。所谓线性探测,就是根据初始 key 的 hashcode 值确定元素在 table 数组中的位置,如果发现这个位置上已经被其他的 key 值占用,则利用固定的算法寻找一定步长的下个位置,依次判断,直至找到能够存放的位置。源代码实现如下:

                          

4.ThreadLocal 的内存泄露是怎么回事?     

最主要的原因在于它的内部类 ThreadLocalMap 中的 Entry 的设计。Entry 继承了 WeakReference<ThreadLocal<?>>,即 Entry的 key 是弱引用。因此如果 ThreadLocal 没有外部强引用来引用它,那么 ThreadLocal 会在下次 JVM 垃圾收集时被回收。这个时候 Entry 中的 key 已经被回收,但是 value 又是一强引用不会被垃圾收集器回收,这样 ThreadLocal 的线程如果一直持续运行,value 就一直得不到回收,这样就会发生内存泄露。

5. ThreadLocalMap 的 key 是弱引用的原因?

我们知道 ThreadLocalMap 中的 key 是弱引用,而 value 是强引用才会导致内存泄露的问题,至于为什么要这样设计,这样分为两种情况来讨论:

  • key 使用强引用:这样会导致一个问题,引用的 ThreadLocal 的对象被回收了,但是 ThreadLocalMap 还持有 ThreadLocal 的强引用,如果没有手动删除,ThreadLocal 不会被回收,则会导致内存泄漏。
  • key 使用弱引用:这样的话,引用的 ThreadLocal 的对象被回收了,由于 ThreadLocalMap 持有 ThreadLocal 的弱引用,即使没有手动删除,ThreadLocal 也会被回收。value 在下一次 ThreadLocalMap 调用 set、get、remove 的时候会被清除。

比较以上两种情况,我们可以发现:由于 ThreadLocalMap 的生命周期跟 Thread 一样长,如果都没有手动删除对应 key,都会导致内存泄漏,但是使用弱引用可以多一层保障,弱引用 ThreadLocal 不会内存泄漏,对应的 value 在下一次 ThreadLocalMap 调用 set、get、remove 的时候被清除,算是最优的解决方案。最佳实践是,应该在我们不使用的时候,主动调用remove方法进行清理。示例代码如下:       

抽象为:

try {
    // 其它业务逻辑
} finally {
    threadLocal对象.remove();
}

6.ThreadLocal 的应用场景有哪些?

  • 保存线程上下文信息,在任意需要的地方可以获取!!!
  • 线程安全的,避免某些情况需要考虑线程安全必须同步带来的性能损失!!!

       保存线程上下文信息,在任意需要的地方可以获取!!!由于ThreadLocal的特性,同一线程在某地方进行设置,在随后的任意地方都可以获取到。从而可以用来保存线程上下文信息。常用的比如每个请求怎么把一串后续关联起来,就可以用ThreadLocal进行set,在后续的任意需要记录日志的方法里面进行get获取到请求id,从而把整个请求串起来。还有比如Spring的事务管理,用ThreadLocal存储Connection,从而各个DAO可以获取同一Connection,可以进行事务回滚,提交等操作。

       线程安全的,避免某些情况需要考虑线程安全必须同步带来的性能损失!!!ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。但是ThreadLocal也有局限性,我们来看看阿里规范:

                 

        这里把ThreadLocal定义为static还有一个好处就是,由于ThreadLocal有强引用在,那么在ThreadLocalMap里对应的Entry的键会永远存在,那么执行remove的时候就可以正确进行定位到并且删除。

        每个线程往ThreadLocal中读写数据是线程隔离,互相之间不会影响的,所以ThreadLocal无法解决共享对象的更新问题!由于不需要共享信息,.自然就不存在竞争问题了,从而保证了某些情况下线程的安全,以及避免了某些情况需要考虑线程安全必须同步带来的性能损失。这类场景阿里规范里面也提到了:

                

7. ThreadLocalMap 源码解析

                                                                

ThreadLocalMap是一个Map,key是ThreadLocal,value是Object。映射到源码就是如下所示:ThreadLocalMap是ThreadLocal的一个静态内部类:

public class Thread implements Runnable{
    ThreadLocal.ThreadLocalMap threadLocals = null;
}

                                   

往ThreadLocalMap里面放值:

public void set(T value){
    Thread t = Thread.currentThread();
    ThreadLocalMap map = t.threadLocals;
    if(map != null){
        map.set(this, value);
    }else{
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
}

往ThreadLocalMap里面取值:

//ThreadLocal类里的方法
public T get(){
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if(map != null){
        ThreadLocalMap.Entity e = map.getEntry(this);
        if(e != null){
            @SuppressWarning("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

8. Thread 和 ThreadLocal 之间的联系

       Thread和ThreadLocal是绑定的, ThreadLocal依赖于Thread去执行, Thread将需要隔离的数据存放到ThreadLocal(准确的讲是ThreadLocalMap)中, 来实现多线程处理。

9. ThreadLocal 在Spring 中的应用

       ThreadLocal 天生为解决相同变量的访问冲突问题, 所以这个对于 Spring 的默认单例 bean 的多线程访问是一个完美的解决方案。Spring 也确实是用了 ThreadLocal 来处理多线程下相同变量并发的线程安全问题。 

       在Spring 中保证数据库事务在同一个连接下执行也使用了 ThreadLocal 。要想实现jdbc事务, 就必须是在同一个连接对象中操作, 多个连接下事务就会不可控, 需要借助分布式事务完成。在 Spring 中 DataSourceTransactionManager 是 Spring 的数据源事务管理器, 它会在你调用 getConnection() 的时候从数据库连接池中获取一个 connection, 然后将其与 ThreadLocal 绑定, 事务完成后解除绑定。这样就保证了事务在同一连接下完成。概要源码如下:

事务开始阶段:

              

事务结束阶段:

                    

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值