ThreadLocal源码解析与理解

一、ThreadLocal的使用

​ ThreadLoca是一个本地的线程变量,他能够做到线程与线程之间的隔离,它其中填充的变量只属于本线程,它为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。

​ 他的用法也非常简单:

ThreadLocal<String> threadLocal=new ThreadLocal<>();
threadLocal.set("1");
threadLocal.get();
threadLocal.remove();

以上的api就是ThreadLocal的主要api,主要就是这三个,一个是设置线程变量,一个是获取,最后是清空。ThreadLocal的泛型的类型就是所要存储对象的类型。set(“1”)执行以后就相当于在本线程中存在了一个存储着“1”这个字符串的一个变量,该变量只对本线程可见。

二、ThreadLocal源码分析
1.set方法实现分析
public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

在set方法中首先获取到了当前的线程t,然后通过getMap方法将t传入获得了一个ThreadLocalMap。来看下getMap的代码:

ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

最终返回了一个t中的变量threadLocals也就是说这个变量是属于线程Thread类的,因为t是当前线程对象。然后看下Thread中的这个变量的定义:

/* ThreadLocal values pertaining to this thread. This map is maintained
 * by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;

默认是个null值,回到set方法,做了个map!=null的判断,因为初始化的时候这个map是个null所以会走else分支,执行 createMap(t, value)方法。在这个方法中最终创建了一个ThreadLocalMap并且赋值给threadLocals。在创建的时候传入了一个this作为key然后传入的值作为value,此时的this就是当前threadLocal对象。最终也就是创建了一个map并且map中的key为当前threadLocal对象,value为传入的数据。

void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

然后看下ThreadLocalMap的构造方法,

/**
 * Construct a new map initially containing (firstKey, firstValue).
 * ThreadLocalMaps are constructed lazily, so we only create
 * one when we have at least one entry to put in it.
 */
ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
    table = new Entry[INITIAL_CAPACITY];
    int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
    table[i] = new Entry(firstKey, firstValue);
    size = 1;
    setThreshold(INITIAL_CAPACITY);
}

会发现在其中创建了一个Entry数组,数组的大小为初始化容量16,如果点击进去会发现Entry类继承了一个虚引用。然后将key进行hash计算。最终执行了一个setThreshold方法扩大了threshold阈值。也就是将来这个参数作为扩容的标志,如果map的长度大于了总长度的三分之二就要进行扩容。至此形成了一个图:
在这里插入图片描述

​ 图中左侧是虚拟机栈右侧是堆,左侧存放的是对象的引用,右侧是实际的对象开辟的内存空间,当创建了一个ThreadLocal对象以后,栈中的引用是强指向的关系,同时通过上边的分析得知,这个ThreadLocal对象也作为了ThreadLocalMap的key,然后ThreadLoacalMap的主要数据结构就是一个Entry数组,所以也就是Entry中的某个entry中的key指向了创建的ThreadLocal对象,由于entry继承了WeakReference类,同时将其key的指向改变成弱引用,也就是最终形成了key到ThreadLocal对象的引用变成了若引用指向。

​ 在栈中还有一个当前线程对象的引用,它指向了堆中的线程对象,而真正的ThreadLocalMap正是存储在每个线程的threadLocals变量中,也就是threadLocals指向了这个map,也就印证了为什么实现了线程之间的隔离,就是因为这个map是放在每个线程之中的。

​ 这样设置成虚引用的好处是什么?这是因为当方法执行结束以后,栈中的数据会随之弹栈,然后这时候栈中的引用也就不存在了,如果key到ThreadLocal对象是一个强引用那么此时ThreadLocal对象则不能被回收,但是此时如果线程还没有销毁,也就是线程的引用还存在着,那么ThreadLocalMap的也不会被回收,但是显然这个map不会被使用了,因为栈中的ThreadLocal的引用已消失,也就是获取不到key值了,所以随着这样的情况反复出现就会造成内存泄漏,而如果设置成弱引用就会尽量避免key值得内存泄漏,因为key是若引用指向了ThreadLocal对象所以在下次GC发生时就会回收掉ThreadLocal对象。但是这样也避免不了value得内存泄漏的发生,所以在阿里规约中约定必须要使用ThreadLocal中得remove方法进行map的移除操作。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值