Loading

ThreadLocal 原理

😉 本文共870字,阅读时间约4min

作用

  • 提供线程内部的局部变量
    • 线程并发: 在多线程并发的场景下,每个线程的变量都是独立的,不会相互影响
    • 传递数据: 我们可以通过ThreadLocal在同一线程,不同组件中传递公共变量
synchronized ThreadLocal
原理 时间换空间 空间换时间
侧重 多个线程之间访问资源的同步性 多线程中让每个线程之间的数据相互隔离

数据结构 & ThreadLocalMap

数据结构

  • 现时JDK8 ThreadLocal的设计是:
    • 每个Thread线程内部都有一个ThreadLocalMap
    • Map里面存储ThreadLocal对象(key)和线程的变量副本(value)。实际上key并不是ThreadLocal本身,而是它的一个弱引用

image-20230206213902570

  • 相比JDK7 结构优点:
    • Thread销毁之后,对应的ThreadLocalMap也会随之销毁,能减少内存使用。

image-20230206213017484

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()方法
posted @ 2023-02-06 21:56  iterationjia  阅读(37)  评论(0编辑  收藏  举报