ThreadLocal类详解

ThreadLocal对象可以提供线程局部变量,每个线程Thread拥有一份自己的副本变量,多个线程互不干扰。

  ThreadLocal.ThreadLocalMap threadLocals = null;
//通过线程继承在异步场景下传递线程本地变量的值
  ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

注:以上代码为Thread类的成员变量。

1. ThreadLocal类数据结构

 ThreadLocalMap的key并不是ThreadLocal本身,而是它的一个弱引用。

2.垃圾回收后key值状态

下图展示了 ThreadLocalMap的引用关系:

可以看到,key虽然是 ThreadLoacl的弱引用,但ThreadLocalMap强引用键值对,因此垃圾回收后有一下两种情况:

//垃圾回收后key值为null,导致内存泄漏
new ThreadLocal<>().set("def");

//垃圾回收时,ThreadLocal强引用仍然存在,key不为null
ThreadLocal<Object> threadLocal = new ThreadLocal<>();
threadLocal.set("def");

3.ThreadLocalMap如何解决Hash 冲突?

ThreadLocalMap 中并没有链表结构,发生Hash冲突时会线性向后查找。

4.ThreadLocalMap.set()方法

1. 使用hash算法得到value的key值

2. 得到槽位为空直接放数据,不为空则继续向后探查

3.若在探查到过期数据之前就探查到entry为null或者key相等的槽位,则进行数据新增或者替换

4.若先探查到了过期数据槽位staleSlot,则需要进行探测式数据清理工作,即向前迭代,得到过期数据的起始下标(遇到entry为null的槽位停止迭代)

5.结束过期元素探查后,继续向后遍历,如果找到key相等的entry数据,进行数据替换,并与当前过期槽位staleSlot交换位置,如果先遍历到entry为null的槽位,则创建新的Entry,替换stableSlot槽位

5.过期key的处理方法

1. 探测式清理

set()方法触发的过期key清理方法就是探测式清理操作

2. 启发式清理

在探测式清理过后如果除了staleSlot以外,还发现了其他过期的slot数据,就要进行启发式清理

启发式清理流程 

注意:启发式清理操作中会调用探测式清理操作 

5.ThreadLocalMap的扩容机制

ThreadLocalMap.set()方法的最后,如果执行完启发式清理工作后,未清理到任何数据,且当前散列数组中Entry的数量已经达到扩容阈值(len*2/3),就开始执行rehash()

if (!cleanSomeSlots(i, sz) && sz >= threshold)
    rehash();

rehash()具体实现:

private void rehash() {
    expungeStaleEntries();

    if (size >= threshold - threshold / 4)
        resize();
}

 注意:rehash()的阈值是size >= threshold,resize()的阈值是size >= threshold*3/Local

由于ThreadLocalMap使用数组存放entry,因此每次扩容都需要创建新的数组

6.ThreadLocalMap.get()方法

1.通过查找key值计算出散列表中slot位置,然后该slot位置中的Entry.key和查找的key一致,则直接返回

2. slot位置中的Entry.key和要查找的key不一致,继续往后查找,查找过程中如果遇到过期key,触发一次探测式数据回收操作,继续往后查找,直到遇到entry为null的槽位,说明没有此数据

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值