ThreadLocal真的存在内存泄漏吗?来这里探讨真相吧

文章目录总结后续
背景
一直以项目中就经常使用,但是对他的原理总是迷迷糊糊,每次想要了解,就打开网上看看别人的博客,发现写的都大同小异,然后又不太清楚,导致自己对原理总是一知半解,今天总算有空来自己研究研究,今天就带领大家来探讨久违的真相
应用场景
这里先介绍下的应用场景 。
直接看源码上的注释(ps:我jdk版本11)
可以看到作者是出自大师 Josh Bloch 和 Doug Lea 之手,这段话的说的大概就是:
提供线程局部变量 。这些变量不同于它们的普通对应变量,访问某个变量(通过其 get 或 set 方法)都是每个线程自己的、独立初始化的变量副本 。实例通常是类中的字段 。
这段话看的迷迷糊糊,大致意思就是中的变量是每个线程独享的,在这个线程中任何地方都能获取到 。应用场景大致有:
获取用户信息,我们在web中不同的层级中要获取用户信息,可以将用户信息保存在一个中,然后在任何地方都能获取到zull网关中的一些请求过滤链中的一下上下文直接的传递,也是通过有时候需要做一些aop拦截方法获取方法参数,也可以使用 。
总之应用场景非常之多
代码演示
这里我们使用一个简单的代码来验证是线程隔离的
private static final ThreadLocal threadLocal = ThreadLocal.withInitial(() -> 0);public static void main(String[] args) {ExecutorService executorService = Executors.newFixedThreadPool(5);for (int i = 0; i < 10; i++) {executorService.submit(() -> {Integer integer = threadLocal.get();System.out.println(Thread.currentThread().getName() + ": " + integer);threadLocal.set(integer + 1);});}executorService.shutdown();}
可以看到每个线程获取到的变量都是独立的,这里只是为了证明线程间变量的独立性,实际不会是这种应用场景,也不会这么使用
自我设计
假设要我们实现这种线程变量隔离的需求,我们需要怎么实现呢?一般也很简单 。使用一个Map,key 为,value为 值
public class MyThreadLocal {private static final Map map = new HashMap<>();public void get() {map.get(Thread.currentThread());}}
这样设计的问题也很明显,不管线程有没有销毁,该线程存储的键值对都不会被回收,因为都在一个map中,除非这个map没有被人引用了 。
jdk中的设计

ThreadLocal真的存在内存泄漏吗?来这里探讨真相吧

文章插图
目前来说jdk中的设计相对巧妙中,他是让每个线程拥有自己的
大致是如下方式:
为什么会是这样的呢?我们直接通过源码来分析:
直接看的get方法
public T get() {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null) {ThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {@SuppressWarnings("unchecked")T result = (T)e.value;return result;}}return setInitialValue();}ThreadLocalMap getMap(Thread t) {return t.threadLocals;}
可以看到就是获取到当前线程t然后通过t去获取,而,可以看到就是调用的属性
所以通过源码我们证明了上面结构的正确性,然后我们来分析这种设计的优点是什么,就是每个线程自己销毁后,不像我们设计的即使线程销毁了,他所拥有的变量还是,这样就避免了内存泄漏 。所以这里我们得出了一个非常重要的结论:
只要当前线程销毁,他所拥有的一定会被回收,所以不存在内存泄漏问题
内存泄漏问题探讨
通过上面的结论我们得出了一个很重要的结论:
只要当前线程销毁,他所拥有的一定会被回收,所以不存在内存泄漏问题