面试官:ThreadLocal 使用场景有哪些?内存泄露问题如何避免?
ThreadLocal 使用场景有哪些?
Thread 类中有两个变量 threadLocals 和 inheritableThreadLocals,二者都是 ThreadLocal 内部类 ThreadLocalMap 类型的变量,我们通过查看内部内 ThreadLocalMap 可以发现实际上它类似于一个 HashMap。在默认情况下,每个线程中的这两个变量都为 null:
只有当线程第一次调用 ThreadLocal 的 set 或者 get 方法的时候才会创建他们。
除此之外,每个线程的本地变量不是存放在 ThreadLocal 实例中,而是放在调用线程的 ThreadLocals 变量里面。也就是说,ThreadLocal 类型的本地变量是存放在具体的线程空间上,其本身相当于一个装载本地变量的载体,通过 set 方法将 value 添加到调用线程的 threadLocals 中,当调用线程调用 get 方法时候能够从它的 threadLocals 中取出变量。如果调用线程一直不终止,那么这个本地变量将会一直存放在他的 threadLocals 中,所以不使用本地变量的时候需要调用 remove 方法将 threadLocals 中删除不用的本地变量,防止出现内存泄漏。
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 并不是为线程保存对象的副本,它仅仅只起到一个索引的作用。它主要是为每一个线程隔离一个类的实例,这个实例的作用范围仅限于线程内部。
评论