ThreadLocal源码分析

ThreadLocal存在线程复用和内存泄漏的问题

什么叫内存泄漏:不会再被使用的对象,但是不会被gc回收.
为什么entry要是weakReference?
TL是强引用,销毁,这时候应该呗gc回收,但是TL的指针还有TLM.Entry中如果entry也是强引用,TL和TLM是循环引用关系,弱引用可以减少内存泄漏风险

而且tl在set和get,remove中都有调用expungeStaleEntry删除null作为key的entry,但是tl是静态的root删不掉
但是TL一般配合线程池使用,一般是静态的是个Root,不可能为null,所以一般要求不用就remove,不remove还会存在复用的风险

前沿知识:引用来设置对象临终前的遗愿,除了强引用之外,软,弱,虚都会指定一个挂在引用点,如果挂在引用点是强引用,跟可达,那么就不gc
extends 子类继承父类,会调用super初始化父类,但是仅仅能使用父类的public成员,Reference利用这一个特性,在Refreence抽象类中定义了私有化成员T referent来作为上层引用

reference强引用:跟可达算法没有就被gc
softReference软引用:内存不足会被gc
weakReference:若引用,一旦gc就没了(图片高速缓存)
phantomreference虚饮用:用来设置gc前的动作,get方法永远返回null和引用队列配合使用,ReferenceQueue配合使用,gc后前会保存到引用队列

ThreadLocal:线程的成员变量线程独享
优于ThreadLocal一般是配合线程池使用的,因为线程池中线程是复用的当做tl的引用节点,不会被gc,使用remove 在finally中remove
感觉类似于JMM堆中的1%的TLAB Thread local allocate buffer在代码级别的实现
创建:

类关系
Thread中有成员属性:ThreadlLocal.ThreadLocalMap threadLocals

ThreadlLocal有静态内部类ThreadLocalMap(单例)
ThreadLocalMap中有静态内部类Entry<ThreadLocal,T> entry是软引用
有个坑点:threadLocalMap的构造方法需要传入当前threadLocal,通过threadLocal的hash值来进行定址(多个threadLocal有可能hash冲突,这时候向后延找空节entry数组节点,如果延到最后都没有空节点触发扩容)

Thread:
成员变量 ThreadlocalMap

class ThreadLocal{

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

Entry(ThreadLocal<?> k, Object v) {
    super(k);//软引用的父类构造方法将ThreadLocal注入进去
    value = v;
}

}
使用:一般声明成为静态的,设置初始化值,防止null空指针
Thread–持有成员变量ThreadLocalMap–tmap持有Entry数组,entry真正持有TL

使用方法:
1.创建threadlocal.withInitial(Supplier默认值),(空构造方法创建的是一个在途的)
2.调用set方法,第一次调用获取当前线程的成员变量ThreadLocalMap是null,显示初始化当前thread的成员变量tMap,存储值到entry,绑定tMap当前threadLocal

3如果后序创建threadlocal,当前线程的TMap非空,

在这里插入图片描述

在这里插入图片描述

待补充点:TMap中的entry默认长度16的数组,TL进来用TL.hashCode&lenth-1,计算槽,如果槽空,直接存储,非空后延存储,
个人认为:thread创建了单例的TLMap(实际上是Entry<Tl,Obj>数组),来存储线程的私有变量(别的线程不可见),达到了线程安全的目的,线程级别的单例模式使用
请添加图片描述

在这里插入图片描述
threadLocal自己申明的使用完毕需要remove,因为线程只要存活,threadLocalMap强引用,threadLocal弱引用gc就没了但是val是强引用,val可能会引发内存泄漏
TL extends WeakReference在出发gc的时候TL就没了,但是线程只要还活着,TLMap。Entry中TL对应的值(强引用)还会存在,造成了entry<null,obj>这种形式的泄漏,所以要显示的去调用remove来清除TL

延伸:TL的remove底层是expungeStaleEntry(),在get和set都会触发这个方法,但是编程的根本不能限制用户,所以又提供了remove,让我们显示调用remove

精确来说,TL配合线程池使用,存在val内存泄漏的隐患的前提是ThreadLoca是强引用,TLMap是一个弱引用,弱引用的指针是指向强引用的,所以TL一直存在,内部的entry也不会给gc,泄漏了
没有线程池,thread用完没了,tlmap无引用指向,gc发生之前还在内存,泄漏,同理val在gc发生之前也存在泄漏

平常可见的其他泄漏hashSet存入对象后修改了参与hash值计算的列,导致查找的时候找不到了,hashset是hashmap实现的entry数组+链表,先hashcode取模,有可能在一个非正常链表这时候找不到了

get源码分析:
public T get() {
//获取当前线程的成员变量ThreadLocal.ThreadLocalMap
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
//如果entry
if (map != null) {
//根据自己当做key去map中找对应的val
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings(“unchecked”)
T result = (T)e.value;
return result;
}
}
//不存在就初始化,根据自己创建TLM,entry的key是自己,val是初始化值
return setInitialValue();
}

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值