ThreadLocal
为 Java 并发安全提供了一种新的思路,它采用线程隔离的机制,每个线程都有拥有自己独立的 ThreadLocal
副本,线程之间互不干扰,每个线程都可以独立地、安全地操作这些变量,而不会影响其他线程。
ThreadLocal
在实际工作中还是有比较多的应用场景,典型的有如下几个:
- 数据库连接或会话信息:很多 ORM 框架,如 Hibernate、Mybatis,都是使用
ThreadLocal
来存储和管理数据库会话的。这样就可以每个线程都有自己独立的的数据库连接,避免了多线程之间的数据库连接冲突。 - 用户身份认证:用户登录成功后,一种常规的做法是是将用户信息存储在 Session 中,如果我们需要获取用户的登录信息,就需要先通过 HttpServletRequest 获取到 Session,然后才能通过 Session 获取到用户信息,同时我们需要在每一个需要用户信息的接口都要加上这个参数,这种方式就显得稍微麻烦了点。换一种思路,我们是否可以将用户的登录信息存储在 ThreadLocal 中呢?当用户登录成功后,我们将用户信息存入到 Token 中返回给前端,当用户调用需要授权的接口时,在 header 中携带 Token ,然后在拦截器中解析 Token,获取用户信息,最后将其存入到 ThreadLocal 中,在使用时我们只需要调用工具类的
get()
就可以获取到用户信息,非常方便,需要注意当请求结束后,记得调用remove()
。 - PageHelper 分页:我们在使用 PageHelper 进行分页时,会用这个方法
PageHelper.startPage(page, limit);
,这个方法就是页码和页码大小等相关信息存储在 ThreadLocal 中,然后在执行分页时读取这些信息。 - 请求追踪(TraceId):在分布式系统中,一个请求可能会跨越多个服务,为了追踪这个请求的整个处理流程,通常会生成一个唯一的 traceId,并在处理请求的每个环节中传递这个 traceId。我们使用
ThreadLocal
存储 traceId ,可以在一个服务的处理过程中保持traceId
不变,即使这个处理过程跨越多个线程或方法调用。 - 日志记录:类似于请求追踪,
ThreadLocal
可以用来在日志记录中保持一些上下文信息(如用户ID,请求ID等),这样可以在整个请求处理流程中轻松地将这些上下文信息添加到日志中,而不需要在每个方法调用中传递这些信息。比如大明哥就喜欢给所有请求都加一个业务流水号 businessNo,然后将其存储在 ThreadLocal,便于打印在日终中,这样就可以通过这个 businessNo 跟踪整个请求日志相关信息了,非常方便。