什么是ThreadLocal?
ThreadLoal 变量,线程局部变量,同一个 ThreadLocal 所包含的对象,在不同的 Thread 中有不同的副本。这里有几点需要注意:
- 因为每个 Thread 内有自己的实例副本,且该副本只能由当前 Thread 使用。这是也是 ThreadLocal 命名的由来。
- 既然每个 Thread 有自己的实例副本,且其它 Thread 不可访问,那就不存在多线程间共享的问题。
以上就不多说了,百度一下多的是这个描述
怎么使用ThreadLocal?
public static void main(String[] args) {
ThreadLocal local = new ThreadLocal();
local.set("fafdasfsadf");//设置值
}
分析源代码来帮助我们了解
我们来看一下ThreadLocal的set方法
ThreadLocal.java
-----------------------------------------------------------------------
public void set(T value) {
Thread t = Thread.currentThread(); //获取当前线程
ThreadLocalMap map = getMap(t); //从当前线程里面获取threadLocalMap
if (map != null)
// this是当前ThreadLocal对象作为key, value是我们传来的值
map.set(this, value);
else
createMap(t, value);
}
//getMap(t) t是当前线程
ThreadLocalMap getMap(Thread t) {
return t.threadLocals; // 返回当前线程的局部变量 threadLocals
}
Thread.java
----------------------------------------------------------------------
//Thread里面定义了一个变量名称:threadLocals 类型为 ThreadLocalMap
ThreadLocal.ThreadLocalMap threadLocals = null;
梳理一下逻辑
1. 当前ThreadLocal对象作为key值
2.保存到我们的当前线程的一个threadLocals的局部变量里面
3.这个threadLocals变量其实是一个ThreadLocal里面定义的一个静态的内部类:ThreadLocalMap
4.这个map的key是弱引用
分析
ThreadLocal local = new ThreadLocal();
local.set("fafdasfsadf");
当执行set的时候
1.获取当前运行的线程t
2.拿到当前线程t的局部变量threadLocals(其实就是一个map形式的内部类)
3.把ThreadLocal对象作为key值 保存到当前线程t的局部变量里面
从下图可以清晰的看见 [email protected]对象作为key值( 弱引用 referent) 保存到了我们线程t 的threadLocals局部变量中
这三者的关系就是这样的,看下图应该就很清楚了,一目了然,有没有,嘻嘻
关于弱引用
弱引用的特点是,如果这个对象只存在弱引用,那么发生GC的话就会被回收
local 是栈帧里面的局部变量 指向的堆中[email protected] 的引用地址,所以这里还是强引用
ThreadLocal local = new ThreadLocal();
我们要把local指向的引用设置null ,就会引发下面的结果
1这下的话 ThreadLocalMap中 key为:[email protected] 就只存在弱引用
2.当发生GC的时候,就会被垃圾收集器回收
3.回收过后该map的key就会变成null
4.看下图可以发现value并没有回收,就出现了key值为null 的数据
如下图 :
关于内存泄漏
内存泄露就是堆内存中不再使用的对象,但是垃圾回收期无法及时的回收(从内存中删除他们的情况),因此他们会被不必要的一直存在,导致内存的浪费。
关于上面弱引用对象被回收,key值为null的情况 - ThreadLocalMap实现中已经考虑了这种情况,在调用 set()、get()、remove() 方法的时候,会清理掉 key 为 null 的记录。
我认为这里的内存泄露是因为ThreadLocalMap随Thread存在而存在,如果只是普通的使用单线程的情况是正常的;
但是如果在线程池的情况下,线程是会被复用的,如果我们不及时remove()的话,就会出现之前定义的ThreadLocalsmap一直都存在,就会造成内存泄漏
建议回收自定义的ThreadLocal变量,尤其在线程池场景下,线程经常会被复用,如果不清理自定义的 ThreadLocal变量,可能会影响后续业务逻辑和造成内存泄露等问题
关于上面的key值名称是referent
因为ThreadLocalMap是Thread自定义的Map
key是弱引用,所以上面key值的名称是referent