ThreadLocal原理学习

什么是ThreadLocal

ThreadLocal是一种数据类型,类似HashMap的key : value存储。主要用来存储线程独有的变量,各线程间互不干扰。通过get、set方法存入读取数据,操作简单。

常用场景:保存用户登录信息,可以在任意地方获取到用户信息。

ThreadLocal、ThreadLocalMap、Thread

ThreadLocal担任get、set、remove等操作方法。ThreadLocalMap是底层数据结构,存储数据,使用动态数组Entry[]实现,它是ThreadLocal的一个内部类。Thread维护了一个类变量ThreadLocal.ThreadLocalMap,其他线程不可访问。

数据结构

引用自: 什么是ThreadLocal_threadlocal hashmap 区别-CSDN博客

Thread维护threadLocals变量,threadLocals是线程变量,保证了各线程间不会相互干扰。ThreadLocalMap是一个默认大小16的Entry数组,虽然它也是一种Map结构,但它并没有实现Map接口。执行set方法时对ThreadLocal对象进行hash算法,决定在数组中的存储位置,当发生hash冲突时会直接往数组的后面进行移位,当到达数据最后一位时还没有找到空位置刚从数组首位继续查找可存储位置。当数组长度达到负载因子2/3时会进行扩容操作。

为什么要用ThreadLocalMap

ThreadLocal的set方法只有一个value参数,为什么要用key : value的Map形式数据结构呢?

一个ThreadLocal虽然只属于一个线程所有,但一个线程可以有多个ThreadLocal对象,为了避免多个ThreadLocal之前的干扰,ThreadLocalMap需要以ThreadLocal对象为key来存储。

内存泄露

首先我们明白,强引用在发生gc时不能回收的,当某个对象只有弱引用时,发生gc时该对象是可以被回收的。

static class Entry extends WeakReference<ThreadLocal<?>> {
    /** The value associated with this ThreadLocal. */
    Object value;

    Entry(ThreadLocal<?> k, Object v) {
	    // 这里key使用的是父类弱引用的构造方法
        super(k);
        value = v;
    }
}

通过源码我们可以知道,ThreadLocalMap中Entry的key就是一个弱引用,而value是强引用,jdk开发者为什么这样设计呢,通过下面这个图很好明白为什么要这样设计。

r1、r2、r3、r4、r5代表强引用,r6代表弱引用

当线程A启动时在Stack中会创建一个线程栈,并指向在Heap中创建的Thread对象。Thread对象引用了ThreadLocalMap对象。ThreadLocalMap指向了一个Entry对象,该Entry对象的key指向了ThreadLocal, Entry的value是set的值。Entry对象如果要被GC回收必需满足r4和r6引用都失效,而ThreadLocal是静态类是不会被回收的,所以如果r6引用使用强引用则Entry对象永远不会被回收。若r6使用弱引用,当线程执行完毕后调用remove方法时会清除r4引用,则Entry对象可以顺利被回收,避免内存溢出风险。

注:我们要在不使用某个ThreadLocal对象后,手动调用remoev方法来删除它,尤其是在线程池中,不仅仅是内存泄露的问题,因为线程池中的线程是重复使用的,意味着这个线程的ThreadLocalMap对象也是重复使用的,如果我们不手动调用remove方法,那么后面的线程就有可能获取到上个线程遗留下来的value值,造成bug。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值