写点什么

面试官:ThreadLocal 使用场景有哪些?内存泄露问题如何避免?

作者:程序员啊叶
  • 2022 年 7 月 28 日
  • 本文字数:1394 字

    阅读完需:约 5 分钟

面试官:ThreadLocal使用场景有哪些?内存泄露问题如何避免?

ThreadLocal 使用场景有哪些?

Thread 类中有两个变量 threadLocals 和 inheritableThreadLocals,二者都是 ThreadLocal 内部类 ThreadLocalMap 类型的变量,我们通过查看内部内 ThreadLocalMap 可以发现实际上它类似于一个 HashMap。在默认情况下,每个线程中的这两个变量都为 null:

ThreadLocal.ThreadLocalMap threadLocals = null;ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
复制代码

只有当线程第一次调用 ThreadLocal 的 set 或者 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;}
复制代码

除此之外,每个线程的本地变量不是存放在 ThreadLocal 实例中,而是放在调用线程的 ThreadLocals 变量里面。也就是说,ThreadLocal 类型的本地变量是存放在具体的线程空间上,其本身相当于一个装载本地变量的载体,通过 set 方法将 value 添加到调用线程的 threadLocals 中,当调用线程调用 get 方法时候能够从它的 threadLocals 中取出变量。如果调用线程一直不终止,那么这个本地变量将会一直存放在他的 threadLocals 中,所以不使用本地变量的时候需要调用 remove 方法将 threadLocals 中删除不用的本地变量,防止出现内存泄漏。

public void set(T value) {        Thread t = Thread.currentThread();        ThreadLocalMap map = getMap(t);        if (map != null)            map.set(this, value);        else            createMap(t, value);}public void remove() {         ThreadLocalMap m = getMap(Thread.currentThread());         if (m != null)             m.remove(this);}
复制代码

ThreadLocal 内存泄露问题如何避免?

每个 Thread 都有一个 ThreadLocal.ThreadLocalMap 的 map,该 map 的 key 为 ThreadLocal 实例,它为一个弱引用,我们知道弱引用有利于 GC 回收。当 ThreadLocal 的 key == null 时,GC 就会回收这部分空间,但是 value 却不一定能够被回收,因为他还与 Current Thread 存在一个强引用关系,如下


由于存在这个强引用关系,会导致 value 无法回收。如果这个线程对象不会销毁那么这个强引用关系则会一直存在,就会出现内存泄漏情况。所以说只要这个线程对象能够及时被 GC 回收,就不会出现内存泄漏。如果碰到线程池,那就更坑了。 那么要怎么避免这个问题呢? 在前面提过,在 ThreadLocalMap 中的 setEntry()、getEntry(),如果遇到 key == null 的情况,会对 value 设置为 null。当然我们也可以显示调用 ThreadLocal 的 remove()方法进行处理。 下面再对 ThreadLocal 进行简单的总结:

  • ThreadLocal 不是用于解决共享变量的问题的,也不是为了协调线程同步而存在,而是为了方便每个线程处理自己的状态而引入的一个机制。这点至关重要。

  • 每个 Thread 内部都有一个 ThreadLocal.ThreadLocalMap 类型的成员变量,该成员变量用来存储实际的 ThreadLocal 变量副本。

  • ThreadLocal 并不是为线程保存对象的副本,它仅仅只起到一个索引的作用。它主要是为每一个线程隔离一个类的实例,这个实例的作用范围仅限于线程内部。

用户头像

还未添加个人签名 2022.07.13 加入

还未添加个人简介

评论

发布
暂无评论
面试官:ThreadLocal使用场景有哪些?内存泄露问题如何避免?_Java_程序员啊叶_InfoQ写作社区