线程本地变量 - ThreadLocal

参考网址: https://www.jianshu.com/p/98b68c97df9b (此处链接不知为何,点击后跳转页面还是本页,建议复制打开)

1、ThreadLocal是什么?

线程本地变量,其为每个使用该变量的线程提供独立的变量副本,所以每个线程都可以独立的改变自己的副本,而不影响其它线程维护的副本。

在每个线程内部都有自己的一个Map,该MapThreadLocal中的一个内部类ThreadLocalMap。在线程对其进行读写时,是从本身拥有的成员变量threadlocals(类型为内部类ThreadLocalMap)中进行读写,该变量是用来存储实际变量副本的。存储时键是当前ThreadLocal变量,值是要保存的变量复本。

扩展:Thread中包含两个ThreadLocal中的ThreadLocalMap成员变量,threadLocalsinheritableThreadLocals

2、ThreadLocal是如何为每个线程创建变量的副本的?

在每个线程Thread内部有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,键是当前ThreadLocal变量,值是变量的副本;

初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()set()时,就会对Thread类中的threadLocals进行初始化,并且以当前ThreadLocal变量为键,以ThreadLocal要保存的副本变量为value

3ThreadLocalMap的解释

ThreadLocal的内部类,类似于HashMap,但并没有实现Map接口

初始容量是16Entry<K,V>数组,这里指定KThreadLocal对象,并且注意Entry<K,V>是继承自WeakReference(弱引用,生命周期只能存活到下次GC前)

HashMap最大的不同是,Entry<K,V>没有next,就是说,解决Hash冲突的方式并不是链表的方式,而是采用线性探测法,简单的步长加一或减一(寻找相邻的位置)。该线性探测法解决hash冲突的效率低。

线性探测法:根据初始keyhashcode值确定元素的位置,若该位置已存在一个元素,则根据一定的算法机制去下一处位置,直到存放元素成功。

4ThreadLocalget()set()方法解释

get()

获取当前线程的ThreadLocalMap对象ThreadLocals

map中获取线程存储的Entry<K,V>节点;

Entry<K,V>节点获取存储的Value副本值返回;

map为空的话返回初始值null,即线程变量副本为null,在使用时需要注意判断NullPointerException

set()

获取当前线程的成员变量map

map非空,则重新将ThreadLocal和新的value副本放入到map中;

map空,则对线程的成员变量ThreadLocalMap进行初始化创建,并将ThreadLocalvalue副本放入map中。

5ThreadLocal的问题,会造成内存泄露

ThreadLocalMap中的键是弱引用的,而value是强引用的,这样会导致ThreadLocal在没有外部对象强引用时,发生GC时弱引用key会被回收,而value不会被回收。此时创建ThreadLocal的线程还在运行,时间过长后,加之Entry对象的value一直得不到回收,发生内存泄露。

为此,在调用ThreadLocalget()set()后,需要调用remove()方法,将Entry节点和Map的引用关系溢出,这样整个Entry对象在GC Roots分析后就变成不可达了,下次GC时就可以完成正确的回收。

6ThreadLocal的应用场景

只适用于独立副本的情况,例如数据库连接、Session管理等

猜你喜欢

转载自blog.csdn.net/m0_37461645/article/details/84866009