ThreadLocal
的 key 是弱引用,那么在 ThreadLocal.get()
的时候,发生GC之后,key 是否为null?
弱引用:
Thread
类有一个类型为ThreadLocal.ThreadLocalMap
的实例变量threadLocals
,也就是说每个线程有一个自己的ThreadLocalMap
。
ThreadLocalMap
有自己的独立实现,可以简单地将它的key
视作ThreadLocal
,value
为代码中放入的值(实际上key
并不是ThreadLocal
本身,而是它的一个弱引用)。每个线程在往
ThreadLocal
里放值的时候,都会往自己的ThreadLocalMap
里存,读也是以ThreadLocal
作为引用,在自己的map
里找对应的key
,从而实现了线程隔离。题目说的是在做
ThreadLocal.get()
操作,证明其实还是有强引用存在的,所以key
并不为null
,ThreadLocal
的强引用仍然是存在的。
ThreadLocal
中ThreadLocalMap
的数据结构?
ThreadLocalMap
有点类似HashMap
的结构,只是HashMap
是由数组+链表实现的,而ThreadLocalMap
中并没有链表结构。
ThreadLocalMap
的Hash 算法?
i
就是当前 key 在散列表中对应的数组下标位置,计算下标 i 最关键的就是利用threadLocalHashguanjianCode
值的计算int i = key.threadLocalHashCode & (len-1);
ThreadLocalMap
中Hash 冲突如何解决?
过期 key 的产生
ThreadLocalMap
的键是弱引用(WeakReference
),当ThreadLocal
对象没有其他强引用时,它会被垃圾回收。- 被垃圾回收的键对应的
Entry
就变成了无效条目(stale entry
),即Entry.key == null
。这些无效条目需要被清理,以释放内存并减少哈希冲突。
核心方法是 线性探测法,并通过以下机制优化性能:
- 弱引用键:允许垃圾回收无效的
ThreadLocal
对象。- 清理无效条目:在插入和访问时清理无效的
Entry
。- 扩容机制:当条目数量过多时,扩展数组容量以减少冲突概率。
ThreadLocalMap
的扩容机制?
扩容后数组长度变为原来的 2 倍。
然后遍历老的散列表,重新计算hash
位置,然后放到新的tab
数组中,如果出现hash
冲突则往后寻找最近的entry
为null
的槽位,遍历完成之后,oldTab
中所有的entry
数据都已经放入到新的tab
中了。
ThreadLocalMap
中过期 key 的清理机制?探测式清理和启发式清理流程?
探测式清理是以当前
Entry
往后清理,遇到值为null
则结束清理,属于线性探测清理。
- 全面清理无效条目,并重新排列有效条目。
- 适用于插入、扩容等场景。
启发式清理是一种轻量级的清理机制,旨在快速处理部分无效条目,而不是对整个哈希表进行全面清理。
启发式清理通常在以下情况下触发:
set()
或get()
操作时,探测到无效条目。- 哈希表中存在较多无效条目,但尚未达到全面清理的条件。