ThreadLocal 底层实现

ThreadLocal 是 Java 中所提供的线程本地存储机制,可以利用该机制将数据 缓存在某个线程内部 ,该线程可以在任意时刻、任意方法中获取缓存的数据。
notion image
ThreadLocal 底层是通过 ThreadLocalMap 来实现的,每个 Thread 对象中都存在一个 ThreadLocalMap 变量,这个 Map 的 key 就是我们要操作的 ThreadLocal 对象,Map 的 value 为需要缓存的值。
我们在使用 ThreadLocal 的时候其实就是拿到当前线程中的 ThreadLocalMap 中的 key 和 value,ThreadLocal 本身不过是个工具类罢了。

注意事项

如果在线程池中使用 ThreadLocal 可能会造成内存泄漏,因为当 ThreadLocal 对象使用完之后,应该要把设置的 key 和 value——也就是 Entry 对象进行回收。但线程池中的线程不会回收,并且 Thread 对象是通过强引用指向 ThreadLocalMap,ThreadLocalMap 也是通过强引用指向 Entry 对象,线程不被回收,Entry 对象也就不会被回收,从而导致不再使用的 Entry 对象越来越多,出现内存泄漏。
解决办法是,在使用了 ThreadLocal 对象之后,手动调用 ThreadLocal 的 remove() 方法,手动清除 Entry 对象。
notion image
实线箭头是强引用,虚线箭头是弱引用
^4efc86
总结:当你在一个方法/栈中创建 ThreadLocal,该对象就被栈强引用着,直到方法执行完毕,栈被销毁,失去了强引用的、只有 ThreadLocalMap.Entry 弱引用的 ThreadLocal 就会被 GC 回收,但 ThreadLocalMap.Entry 中的 value 却实打实强引用了一个对象,entry 不死它就不死,所以说养成良好习惯 new ThreadLocal() 和 ThreadLocal.remove() 要在方法中成对出现。

应用场景

当一个变量是共享的(成员变量、static 等),但是需要每个线程互不影响,相互隔离,就可以使用 ThreadLocal。
  1. 跨层传递信息的时候,每个方法都声明一个参数很麻烦,A、B、C、D 4 个类互相传递,每个方法都声明参数降低了维护性,可以用一个 ThreadLocal 共享变量,在 A 存值, B、C、D 都可以获取。
  1. 隔离线程,存储一些线程不安全的工具对象,如(SimpleDateFormat)。
  1. Spring 中的事务管理器就是使用的 ThreadLocal,这也是为什么声明式事务如果用到了多线程,它是没有办法达到一致性的,因为一个线程就存储了一个事务。
  1. SpringMVC 的 HttpSession、HttpServletReuqest、HttpServletResponse 都是放在 ThreadLocal,因为 servlet 是单例的,而 SpringMVC 允许在 controller 类中通过@Autowired 配置 request、response 以及 requestcontexts 等实例对象,在任何地方获取请求都永远是当前的请求。底层就是搭配 ThreadLocal 才实现线程安全。
浅谈一下HashMapJWT
Loading...