ThreadLocal原理
ThreadLocal简介
ThreadLocal,即线程变量,指ThreadLocal中的变量属于当前线程,并且该变量是线程隔离的。
每个线程都会在 ThreadLocal 中保存一份该线程独有的数据,所以它是线程安全的。
ThreadLocal原理
底层数据结构
ThreadLocal 底层是通过 ThreadLocalMap 这个静态内部类来存储数据的,ThreadLocalMap 就是一个键值对的 Map,它的底层是 Entry 对象数组,Entry 对象中存放的键是 ThreadLocal 对象,值是 Object 类型的具体存储内容。
哈希冲突
ThreadLocal 的散列方式,称为 斐波那契散列 。
虽然这种方式能够充分散列,但还是无法避免出现哈希冲突。
- 如果散列值已存在且 key 为同一个对象,直接更新 value
- 如果散列值已存在但 key 不是同一个对象,尝试在下一个空的位置进行存储
处理哈希冲突的方法:
如果在 set 时遇到哈希冲突,ThreadLocal 会尝试在数组下一个索引位置进行存储,同时在 set 过程中 ThreadLocal 会释放 key 为 NULL,value 不为 NULL 的脏 Entry对象的 value 属性来防止内存泄漏 。
扩容机制
ThreadLocalMap 的初始容量是 16。
// 初始化容量
private static final int INITIAL_CAPACITY = 16;
扩容机制:
- ThreadLocalMap的set(ThreadLocal<?> key, Object value) 方法中可能会触发启发式清理,在清理无效 Entry 对象后,如果数组长度大于等于数组定义长度的 2/3,则首先进行 rehash;
- rehash 会触发一次全量清理,如果数组长度大于等于数组定义长度的 1/2,则进行 resize(扩容);
- 进行扩容时,Entry 数组为扩容为 原来的2倍 ,重新计算 key 的散列值,如果遇到 key 为 NULL 的情况,会将其 value 也置为 NULL,帮助虚拟机进行GC。
内存泄露
Entry 对象的 key 即 ThreadLocal 类是继承于 WeakReference 弱引用类。在发生 GC 活动时,无论内存空间是否足够,垃圾回收器都会回收具有弱引用的对象。
在发生 GC 活动时,若 ThreadLocal 类没有外部强引用,就会将 ThreadLocal 对象回收。
解决:
主动解决:
使用完 ThreadLocal 中存储的内容后将它 remove 掉就可以了。
ThreadLocal内部防止内存泄露:
ThreadLocalMap的rehash() 方法中会对 ThreadLocalMap 进行一次全量清理,全量清理会遍历整个 Entry 数组,删除所有 key 为 NULL,value 不为 NULL 的脏 Entry对象。
使用场景
- 1、每个线程需要有自己单独的实例
- 2、实例需要在多个方法中共享,但不希望被多线程共享
使用场景:
1)存储用户Session
2)数据库连接,处理数据库事务