ThreadLocal-学习

187 阅读3分钟

简介

ThreadLocal相当于线程私有属性(threadLocals)的封装,提供了#get、#set的API。

多线程情况下不会有并发的问题,通常用来做数据传输。如拦截token存储其用户信息,traceId、租户拦截等。

核心点

执行原理

一个线程,可以创建多个ThreadLocal,以每个ThreadLocal对象作为key,存储在当前线程的threadLocals中,类型是ThreadLocal.ThreadLocalMap。

#set方法解析

1. 先获取当前线程的ThreadLocalMap,对应字段为Thread.threadLocals
2. 如果线程的threadLocals为空,新建
3. threadLocals不为空,调用ThreadLocalMap#set
  1. 获取到key的hashCode后计算出在Entry数组中对应的索引idx
  2. 循环Entry数组,起始位置为计算出的索引idx
    3. 如果key相等赋值value返回
    4. 如果key为null,进行替换
    5. 如果未找到,则idx+1,继续寻找。
  6. 循环执行完后还未找到就在idx处,新建entry

TheadLocalMap与HashMap的区别

不同点ThreadLocalMapHashMap
引用key为弱引用key为强引用
结构数组数组 + 链表
解决hash冲突开发地址法(向后+1保存元素)
因为数据量少,而且key又是弱引用,所以使用
链式地址法(将冲突单独保存到单向链表中)

TheadLocalMap,Entry的key为什么要使用弱引用

防止内存泄漏。
弱引用的情况下:如果ThreadLocal为null,则可以将ThreadLocal进行回收,如果是强引用则不会进行回收。

jvm都有哪些引用

引用类型特征实现类
强引用宁愿GC也不会回收
软引用内存不足时回首SoftReference
弱饮用GC时回收WeakReference
虚引用任何时候都可能被GC回收。作用通知对象被回收,回调#finalizePhantomReference

对象引用与GC回收

将对象设为null,对象就会被回收吗?
不会被立即回收,将对象设为null,只是去掉其引用关系,内存对象还在,等GC过后才会回收。

GC判断对象回收有两种:
  1. 引用计数。无法解决循环引用问题,没有被使用。
  2. 引用可达性分析。从GCRoot开始,只要没有被引用到的都可以被回收。
​
GCRoot包括:
 a. 方法执行时,虚拟机栈中引用的对象。
 b. 静态属性属性引用的对象。

内存泄漏

有一些对象其实已经不再使用了,但是GC却回收不了,长期下去会导致内存溢出

为什么ThreadLocal执行完后需要remove

1. 因为ThreadLocalMap中Entry的value为强引用,如果一直不删除,会导致内存溢出。
2. 如果是使用线程池,防止因为线程复用,导致ThreadLocal被错用。

父子线程传值问题

InheritableThreadLocal:JVM自带

怎么解决的父子线程传值:

  1. Thread.inheritableThreadLocals,存储ThreadLocal.ThreadLocalMap
  2. InheritableThreadLocal,会设置Thread.inheritableThreadLocals字段,不会设置threadLocals
  3. 在创建子线程的时候,currentThread作为父线程, 父线程inheritThreadLocals不为空,则复制父类的inheritThreadLocals到子线程。

线程池的问题:

因为线程会复用,只会初始化一次,所以无法获取最新父类的threadLocals.
TransmittableThreadLocal:阿里的

怎么解决线程池复用问题:

Transmittable的GitHub

TransmittableThreadLocal-学习