ThreadLocal
是一个本地线程副本变量工具类。内部是一个弱引用的 Map 来维护。允许你为每个线程创建独立的变量副本,从而避免线程之间的并发问题。在多线程环境中,通常需要确保多个线程不会共享相同的变量,ThreadLocal
正是解决这个问题的一种机制。
1 | public class ThreadLocalExample { |
核心概念
线程局部变量:每个线程都会拥有自己独立的 ThreadLocal
变量副本。一个线程的副本只属于该线程,其他线程无法访问或修改这个副本。
避免共享状态:使用 ThreadLocal
可以避免多线程环境下的共享状态问题,因为每个线程操作的都是自己的变量副本。
线程安全性:ThreadLocal
提供了一种简单的线程安全方式,避免了显式的同步(synchronized
)。
使用场景
数据库连接管理:可以为每个线程分配一个独立的数据库连接对象,以避免多个线程竞争同一个连接资源。
Session 管理:在 Web 应用中,可以使用 ThreadLocal
存储每个用户的 Session 信息。
避免参数传递:在多个方法调用链中,可以使用 ThreadLocal
传递数据,而不需要在方法参数中显式传递。
源码
数据结构
Thread
类有一个类型为 ThreadLocal.ThreadLocalMap
的实例变量 threadLocals
,也就是说每个线程有一个自己的 ThreadLocalMap
。
ThreadLocalMap
有自己的独立实现,可以简单地将它的 key
视作 ThreadLocal
,value
为代码中放入的值(实际上 key
并不是 ThreadLocal
本身,而是它的一个弱引用)。
每个线程在往 ThreadLocal
里放值的时候,都会往自己的 ThreadLocalMap
里存,读也是以 ThreadLocal
作为引用,在自己的 map
里找对应的 key
,从而实现了线程隔离。
内存泄露风险
ThreadLocalMap
中的键是弱引用,值是强引用。即使 ThreadLocal
被垃圾回收了,值仍然可能被持有,导致内存泄漏。所以在不再需要时,应调用 ThreadLocal.remove()
方法清理相关数据。
强引用:我们常常 new 出来的对象就是强引用类型,只要强引用存在,垃圾回收器将永远不会回收被引用的对象,哪怕内存不足的时候
弱引用:使用 WeakReference 修饰的对象被称为弱引用,只要发生垃圾回收,若这个对象只被弱引用指向,那么就会被回收
set() 方法
set() 方法具体流程如下:
- 获取 Thread 中的
ThreadLocalMap
- 如果不存在,则创建新的
ThreadLocalMap
- 如果存在,则设置 map 中的值,key 为当前
ThreadLocal
, value 为要设置的值
1 | /** |