ThreadLocal底层原理详解

引言

由于在项目中使用到了ThreadLocal,所以特地花一篇文章的时间来讲讲ThreadLocal的底层实现

1. 什么是ThreadLocal?

简单来说就是,ThreadLocal是依附在Thread上的局部变量,可以实现线程间的隔离,线程内部的资源共享,ThreadLocal是线程私有的;

2. ThreadLocal的源码分析

在这里插入图片描述

图2.1 Thread内部的ThreadLocalMap属性
如图2.1,Thread内部维护了一个ThreadLocalMap的属性,初始值为null。

那我们再来看看ThreadLocalMap是什么?如图2.2。
在这里插入图片描述

图2.2 ThreadLocal内部的ThreadLocalMap静态内部类

ThreadLocalMap设置为静态的内部类,并且权限修饰符为缺省。所以我们外部无法获得这个内部的对象,只能通过ThreadLocal来操控。并且内部是由Entry数组构成的,每个Entry数组的key是一个弱引用,value则不是弱引用。(后面具体会讲讲它们的作用,为什么这样设计??)

我们再来看看ThreadLocal的内部方法:
在这里插入图片描述

图2.3 ThreadLocal内部的get方法

如图2.3,我们发现如果去get的话,没有get到当前线程的map或者map里没有当前ThreadLocal的键的话,就会初始化一个map,并且将ThreadLocal作为key,null作为value初始化进去(和其他的map实现不同,它的get方法如果get不到的话还会set当前的TheadLocal进去,其他的map实现并没有修改map里的东西);如果获取到了当前线程里的map的话,则将key=ThreadLocal的value值取出来

在这里插入图片描述

图2.4 ThreadLocal内部的set方法

在这里插入图片描述

图2.5 ThreadLocal内部的remove方法

如图2.4,2.5,逻辑与get相比简单,都是大同小异

以上就是ThreadLocal里的常用方法源码。总的来说,实现线程隔离关键的还是ThreadLocalMap,而ThreadLocal更感觉是一种工具或者说是API,用来提供操作ThreadLocalMap的一些方法

补充:
ThreadLocalMap 的一些特点

  • 初始容量 16,扩容因子 2/3,扩容容量翻倍
  • key 索引冲突后用开放寻址法解决冲突

为什么Map的key要设置成弱引用呢??

因为如果我们ThreadLocalMap中的ThreadLocal不设置成弱引用,设置成强引用的话,如果外界已经将所有引用ThreadLocal的地方设置为了null(也就是不再使用了),但是我们的Map里的key还指向堆内存里的ThreadLocal呢,而我们又不能直接操控Map。

并且这个线程始终在运行(比如说线程池复用连接),那么久而久之,堆内存里的ThreadLocal就无法被回收,造成内存泄露

而设计成弱引用的话,在每次GC时,发现没有其他强引用指向ThreadLocal了,便会将其回收

概括说就是:在方法中新建一个线程本地对象,就有一个强引用指向它,在调用set()后,线程的ThreadLocaMap对象里的Entry对象又有一个引用k指向它。如果后面这个引用k是强引用就会使方法执行完,栈帧中的强引用销毁了,对象还不能回收,造成严重的内存泄露.

那为什么不设置成软引用呢??我自己思考的话:应该就是让没有引用的尽快回收,而不用等到内存不够在回收

那还有一个问题:key设置成了弱引用,但是value不是啊??那value岂不是释放不了??

value怎么释放呢?

我们将弱引用为key的释放掉了,但是value还没有释放呢??这样也可能会造成内存泄露。

我思考的是:那我们把value也设置成弱引用不就好了????

那肯定是不行的啊?一般value都没有外界的引用指向它,只有map里有引用,我们把这个引用设置为了弱引用,那不是key可能还在用,我们的value却已经被回收了。

那怎么避免value引起的内存泄露呢??这就需要我们开发者自己手动去remove了。还有就是以下补充的。

补充:
内存释放时机

  • 被动 GC 释放 key
    • 仅是让 key 的内存释放,关联 value 的内存并不会释放
  • 懒惰被动释放 value
    • get key 时,发现是 null key,则释放其 value 内存
    • set key 时,会使用启发式扫描,清除临近的 null key 的 value 内存,启发次数与元素个数,是否发现 null key 有关
  • 主动 remove 释放 key,value
    • 会同时释放 key,value 的内存,也会清除临近的 null key 的 value 内存
    • 推荐使用它,因为一般使用 ThreadLocal 时都把它作为静态变量(即强引用),因此无法被动依靠 GC 回收

由此可见,ThreadLocal为防止内存泄露做了多大努力啊!!!但最好的方法还是remove,其他的情况还是有可能造成内存泄露滴!!!

在个人项目中实现

在这里插入图片描述

为什么我们项目要用ThreadLocal呢?

可以参考下面的文章
快醒醒,Cookie + Session 的时代已经过去了

猜你喜欢

转载自blog.csdn.net/small_engineer/article/details/124813818