ThreadLocal 原理
😉 本文共870字,阅读时间约4min
目录
作用
- 提供线程内部的局部变量
- 线程并发: 在多线程并发的场景下,每个线程的变量都是独立的,不会相互影响
- 传递数据: 我们可以通过ThreadLocal在同一线程,不同组件中传递公共变量
synchronized ThreadLocal 原理 时间换空间 空间换时间 侧重 多个线程之间访问资源的同步性 多线程中让每个线程之间的数据相互隔离
数据结构 & ThreadLocalMap
数据结构
- 现时JDK8
ThreadLocal
的设计是:- 每个Thread线程内部都有一个ThreadLocalMap
- Map里面存储ThreadLocal对象(key)和线程的变量副本(value)。实际上
key
并不是ThreadLocal
本身,而是它的一个弱引用。
- 相比JDK7 结构优点:
Thread
销毁之后,对应的ThreadLocalMap
也会随之销毁,能减少内存使用。
ThreadLocalMap
如何计算bucket位置
-
ThreadLocal使用的是自定义的ThreadLocalMap,并且没有链表。
-
计算bucket位置
index = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1) // 关于firstKey.threadLocalHashCode 每次获取当前值并加上HASH_INCREMENT,HASH_INCREMENT = 0x61c88647,这个值和斐波那契散列有关, 主要目的就是为了让哈希码能均匀的分布在2的n次方的数组里, 也就是Entry[] table中,这样做可以尽量避免hash冲突。
set()时hash冲突的解决
-
ThreadLocalMap使用
开发地址-线性探测法
来解决哈希冲突 -
线性探测法的地址增量di = 1, 2, … 其中,i为探测次数
-
该方法一次探测下一个地址,直到有空的地址后插入。把table看成一个环形数组。
/**
* 获取环形数组的下一个索引
*/
private static int nextIndex(int i, int len) {
return ((i + 1 < len) ? i + 1 : 0);
}
弱引用 & 内存泄露
弱引用好处
- Entry继承WeakReference,使用弱引用。
- 可以将ThreadLocal对象的生命周期和线程生命周期解绑
- 持有对ThreadLocal的弱引用,可以使得ThreadLocal在没有其他强引用的时候被回收掉,这样可以避免因为线程得不到销毁导致ThreadLocal对象无法被回收。
内存泄漏
- 内存溢出:
- 没有足够的内存提供申请者使用。
- 内存泄漏:
- 分配的堆内存由于某种原因未释放或无法释放,造成系统内存的浪费。
- 导致程序运行减慢,内存泄漏的堆积终将导致内存溢出。
ThreadLocal中的内存泄漏
-
ThreadLocalMap
中使用的 key 为ThreadLocal
的弱引用,而 value 是强引用。所以,如果ThreadLocal
没有被外部强引用的情况下,在垃圾回收的时候,key 会被清理掉,而 value 不会被清理掉。 -
这样一来,
ThreadLocalMap
中就会出现 key 为 null 的 Entry。假如我们不做任何措施的话,如果线程不结束,value 永远无法被 GC 回收,这个时候就可能会产生内存泄露。- 只要记得在使用完ThreadLocal及时的调用remove ,无论key是强引用还是弱引用都不会有问题。
ThreadLocalMap
实现中已经考虑了这种情况,在调用set()
、get()
、remove()
方法的时候,会清理掉 key 为 null 的记录。使用完ThreadLocal
方法后 最好手动调用remove()
方法