ThreadLocal底层实现
我的描述。
ThreadLocal 有一个内部类 ThreadLocalMap,这相当于一个hash表。
ThreadLocalMap又作为Thread类对象 的成员,相当于每个 线程都维护了自己的 ThreadLocalMap。
每当往ThreadLocal set值的时候其实是 往当前线程的ThreadLocalMap中添加一个 key,value的映射,key就是ThreadLocal,value就是要保存的变量副本。
ThreadLocal 关键字的作用
ThreadLocal是一个解决线程安全问题的工具,实现了每个线程都有自己的一个空间 存储共享变量(ThreadLocal) 的副本,每个线程去访问操作自己的共享变量副本就行。(有的就线程局部变量)
ThreadLocal 的用处 (重要)
- 线程隔离
- 上下文传递
项目中有用到ThreadLocal吗(面经)
- 配合拦截器,将身份验证信息放入ThreadLocal中,确保线程内任何地方都能安全访问用户信息。 (上下文传递)
ThreadLocal 内存泄露问题?
【Java面试最新】ThreadLocal会出现内存泄露吗?_哔哩哔哩_bilibili
ThreadLocalMap 中使用的 key 为 ThreadLocal 的弱引用,而 value 是强引用。
所以,如果 ThreadLocal 没有被外部强引用的情况下,在垃圾回收的时候,key 会被清理掉,而 value 不会被清理掉。
这样一来,ThreadLocalMap 中就会出现 key 为 null 的 Entry。假如我们不做任何措施的话,value 永远无法被 GC 回收,这个时候就可能会产生内存泄露。
插曲:那Thread线程被回收了,ThreadLocalMap跟着里面的entry 及value 不就也会回收吗?
这样说不完全对,因为当使用线程池的时候,Thread不会被回收而是重复利用,这样就会存在内存泄露, 甚至出现混乱(下一个线程能获取到上一个线程set的值)
但其实,ThreadLocalMap 实现中已经考虑了这种情况,在调用 set()、get()、remove() 方法的时候,会清理掉 key 为 null 的记录。 但最好还是我们手动清理,调用ThreadLocal的remove() 。
ThreadLocalMap自动清理key为null的数据就不会内存泄露了吗?
答: 不能完全避免,那怎么办?
- 每次使用完ThreadLocal后,调用remove() 方法手动清理
- ThreadLocal申明为全局变量,则不会出现最开始说的(ThreadLocal 没有被外部强引用)的情况。
ThreadLocal还有什么内存泄露的问题吗?
- 除了key是弱引用这个情况外。
- 搭配线程池使用时,由于线程池复用线程,导致后续线程可能获取到前面线程set的值。
为什么ThreadLocalMap中key要弱引用?(也是为了避免内存泄露)
下面的描述 前提是ThreadLocal在局部创建时
- 当 ThreadLocal 为局部变量时(在方法中声明),我们在方法中 set 了值,那么线程中的ThreadLocalMap中的entry的key就会指向这个ThreadLocal。
- 如果Entry中的key为强引用,那么当方法执行完毕栈帧销毁(局部变量表中的引用不会继续指向ThreadLocal ) ,方法执行完毕后ThreadLocal不会GC回收,因为线程还存活它被ThreadLocalMap的entry的key强引用了,那么就出现了内存泄露。
- 所以要使用弱引用,但使用弱引用也会带来一点问题(上面已经说了),但其实,ThreadLocalMap 实现中已经考虑了这种情况。
ThreadLocalMap是如何处理哈希冲突的?
线性探测法(不是拉链法)
为什么?
根据拉链法和线性探测法的优缺点,因为ThreadLocalMap中的key是threadLocal对象,所以可能负载因子不会太高,使用简单的线性探测法就行。
线性探测法和拉链法有什么优缺点?
线性探测法:
- 实现简单
- 内存连续,缓存友好,很好地利用空间局部性原理。
但是线性探测缺点:
- 负载因子高(表满的时候) 时 性能下降严重。
拉链法:
- 不会出现负载因子高而性能下降的问题