type
status
date
slug
summary
tags
category
icon
password
ThreadLocal

ThreadLocal 底层实现

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

注意事项

如果在线程池中使用 ThreadLocal 可能会造成内存泄漏,因为当 ThreadLocal 对象使用完之后,应该要把设置的 keyvalue——也就是 Entry 对象进行回收。但线程池中的线程不会回收,并且 Thread 对象是通过强引用指向 ThreadLocalMapThreadLocalMap 也是通过强引用指向 Entry 对象,线程不被回收,Entry 对象也就不会被回收,从而导致不再使用的 Entry 对象越来越多,出现内存泄漏。
解决办法是,在使用了 ThreadLocal 对象之后,手动调用 ThreadLocalremove() 方法,手动清除 Entry 对象。
notion image
实线箭头是强引用,虚线箭头是弱引用
Q:什么是强引用?
A:强引用是 Java 中最常见的引用类型。普通的对象引用就是强引用。例如:
在这个例子中,obj 就是一个强引用,它指向了一个对象。如果一个对象具有强引用,即使内存紧张,垃圾回收器也不会回收这个对象,直到这个对象不再有任何强引用指向它。
^4efc86
Q:什么是弱引用?
A:你可以使用 java.lang.ref.WeakReference 类来创建弱引用:
它与强引用的主要区别在于垃圾回收器的处理方式。如果一个对象只有弱引用指向它,在下一次垃圾回收时,无论内存是否充足,垃圾回收器都会回收该对象。
Q:为啥 value 值不跟 key 一样用弱引用?
A:
  • key 使用弱引用:正常情况下,在线程执行过程中,Thread 本身通常会被其他地方(如线程池、任务队列、栈等)持有强引用,它不会轻易被回收。当线程执行结束且不再有强引用时,Thread 对象连同其持有的 ThreadLocalMap 都会被垃圾回收。由于 ThreadLocalMapkey 是对 ThreadLocal 的弱引用,所以只要 ThreadLocal 没有其他强引用,它也会被 GC 回收。整个 entry(包括 value)也会随 Thread 的回收一起被清理。只有在线程池中,线程被复用而不被回收,才会留下一个 nullkey 和还活着的 value
  • value 使用强引用:当一个线程创建并开始执行时,它通常会使用自己的局部变量(即 ThreadLocal 中的 value),尤其是 web 服务中,这个局部变量应该存在直到线程结束或者完成其任务。使用强引用可以确保局部变量的持久性,直到线程结束或线程池的线程回收。
  • 如果将 value 设置为弱引用,垃圾回收可能会使其还被线程使用时就被回收掉,这将导致线程内部逻辑发生错误。
总结:当你在一个方法/栈中创建 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...