ThreadLocal 是什么
ThreadLocal
是一个用于创建线程局部变量的类。通常情况下,我们创建的变量是可以被任何一个线程访问并修改的。而使用 ThreadLocal
创建的变量只能被当前线程访问,其他线程则无法访问和修改
用法
创建
1 | ThreadLocal<String> threadLocal = new ThreadLocal<>(); |
set
1 | threadLocal.set("Hello World!"); |
get
1 | threadLocal.get(); |
初始值
为 ThreadLocal
设置默认的 get()
返回值,需要重写 initialValue
方法,下面是一段代码,我们将默认值修改成了线程的名字
1 | ThreadLocal<String> threadLocal = new ThreadLocal<String>() { |
实现原理
为了更好的掌握 ThreadLocal
,我认为了解其内部实现是很有必要的,这里我们以 set
方法从起始看一看 ThreadLocal
的实现原理
下面是 ThreadLocal
的 set
方法,大致意思为
- 首先获取当前线程
- 利用当前线程的
ThreadLocalMap
的对象 - 如果
ThreadLocalMap
对象不为空,则设置值,否则创建这个ThreadLocalMap
对象并设置值
1 | /** |
getMap
方法的实现如下
1 | /** |
threadLocals
就是 Thread 的属性
1 | public class Thread implements Runnable { |
如果一开始调用 set
方法,ThreadLocalMap
对象未创建,则新建 ThreadLocalMap
对象,并设置初始值
1 | /** |
所以,ThreadLocal
的值是放入了当前线程的 ThreadLocalMap
属性中,所以只能在本线程中访问,其他线程无法访问
使用ThreadLocal会造成内存泄露吗?
有网上讨论说ThreadLocal会导致内存泄露,原因如下:
- 首先
ThreadLocal
实例被线程的ThreadLocalMap
实例持有,也可以看成被线程持有 - 如果应用使用了线程池,那么之前的线程实例处理完之后出于复用的目的依然存活
- 所以,
ThreadLocal
设定的值被持有,导致内存泄露
面的逻辑是清晰的,可是 ThreadLocal
并不会产生内存泄露,因为 ThreadLocalMap
在选择 key 的时候,并不是直接选择 ThreadLocal
实例,而是 ThreadLocal
实例的弱引用
1 | static class ThreadLocalMap { |
使用场景
在实际项目中,可以用来减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度,因为 Servlet
是单例多线程的,每个请求执行的操作都是同一个线程中。比如:可以用 ThreadLocal
来存每一次请求用户的信息,定义了一个类 UserContextHolder
1 | public class UserContextHolder{ |
当用户每次请求进来时,在拦截器中获取用户信息调用 UserContextHolder.setUser()
将其放到 userContext
中,无论在哪我们只要调用 UserContextHolder.getUser()
可以很轻松的获取到用户的信息,而不用在函数调用时一层一层的传递。同时在拦截器结束时调用 UserContextHolder.remove()
移除掉即可