简介
ThreadLocal 使得每一个线程有自己专属的本地变量。如果创建了一个 ThreadLocal
变量,那么访问这个变量的每个线程都会有这个变量的本地副本,这也是ThreadLocal
变量名的由来。可以使用 get()
和 set()
方法来获取默认值或将其值更改为当前线程所存的副本的值,从而避免了线程安全问题。
原理
ThreadLocalMap
从 Thread 类的源码入手,Thread 类中有一个 threadLocals 和 inheritableThreadLocals 变量,二者都是 ThreadLocalMap 类型的变量。默认情况下这两个变量都是 null,只有当前线程调用 ThreadLocal
类的 set
或get
方法时才创建它们,实际上调用这两个方法的时候,我们调用的是ThreadLocalMap
类对应的 get()
、set()
方法。
1 | public class Thread implements Runnable { |
ThreadLocal 原理
从 ThreadLocal 类的 set 方法可以看出,通过 Thread.currentThread 获取当前线程对象,再通过 getMap 获取到当前线程对象的 ThreadLocalMap 对象。
所以,最终变量是放在当前线程的 ThreadLocalMap 中,并不是存储在 ThreadLocal 对象中,ThreadLocal 仅仅是 ThreadLocalMap 的封装。
1 | public void set(T value) { |
每一个线程对象都有一个 ThreadLocalMap,可以存储以 ThreadLocal 为 key,Object 对象为 value 的键值对。
内存泄漏问题
ThreadLocalMap 中的 key 为弱引用,而 value 为强引用。如果 ThreadLocalMap 在没有被外部强引用的情况下,在垃圾回收的时候,key 会被清理掉,而 value 不会被清理掉。
这样以来,ThreadLocalMap 就会出现 key 为 null 的 Entry,如果我们不做任何措施的话,value 永远无法被 GC 回收,这个时候就可能会产生内存泄露。不过 ThreadLocalMap 已经考虑了这种情况,在调用 get、set、remove 方法时,会清理掉 key 为 null 的记录。