什么是 ThreadLocal 如何应用ThreadLocal是 Java 提供的一种线程本地变量机制。它可以让同一个变量在不同线程中保存各自独立的副本。核心特点同一个ThreadLocal变量在不同线程中对应不同的值。线程 A 存进去的值线程 B 读取不到。线程 B 存进去的值线程 A 也读取不到。适合保存和当前线程强相关的上下文信息例如当前登录用户、请求追踪 ID 等。典型应用场景在 Java Web 项目中一个 HTTP 请求通常会被 Tomcat 线程池中的某个工作线程处理。请求处理流程大致如下用户请求 ↓ Tomcat 分配工作线程 ↓ Filter / Interceptor ↓ Controller ↓ Service ↓ DAO / Mapper如果在拦截器中解析出了当前登录用户并且希望后续的Controller、Service都能方便地获取用户信息而不是在每个方法中层层传参就可以使用ThreadLocal保存当前用户。由于同一个请求通常会在同一个线程中执行所以后续业务代码可以从ThreadLocal中取到同一份用户信息。示例代码定义用户上下文工具类publicclassUserContext{privatestaticfinalThreadLocalUserDTOUSER_HOLDERnewThreadLocal();publicstaticvoidsetCurrentUser(UserDTOuser){USER_HOLDER.set(user);}publicstaticUserDTOgetCurrentUser(){returnUSER_HOLDER.get();}publicstaticvoidclear(){USER_HOLDER.remove();}}在拦截器中写入用户信息ComponentpublicclassLoginInterceptorimplementsHandlerInterceptor{OverridepublicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{Stringtokenrequest.getHeader(Authorization);// 假设这里已经根据 token 查询到了用户信息UserDTOuserDTOnewUserDTO();userDTO.setId(1001L);userDTO.setUsername(sihan);UserContext.setCurrentUser(userDTO);returntrue;}OverridepublicvoidafterCompletion(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler,Exceptionex)throwsException{UserContext.clear();}}在 Service 中读取用户信息ServicepublicclassOrderService{publicvoidcreateOrder(){UserDTOcurrentUserUserContext.getCurrentUser();LonguserIdcurrentUser.getId();// 根据当前用户创建订单}}注意事项Tomcat 使用线程池处理请求。线程在处理完一个请求后不会立即销毁而是会被复用去处理后续请求。因此如果使用ThreadLocal保存了用户信息却没有在请求结束后调用remove()清理数据可能会导致以下问题下一个请求错误读取到上一个请求的用户信息。长时间持有对象引用增加内存泄漏风险。所以在 Web 项目中使用ThreadLocal时一定要在请求结束阶段清理数据常见位置是拦截器的afterCompletion方法。
什么是 ThreadLocal ?如何应用?
什么是 ThreadLocal 如何应用ThreadLocal是 Java 提供的一种线程本地变量机制。它可以让同一个变量在不同线程中保存各自独立的副本。核心特点同一个ThreadLocal变量在不同线程中对应不同的值。线程 A 存进去的值线程 B 读取不到。线程 B 存进去的值线程 A 也读取不到。适合保存和当前线程强相关的上下文信息例如当前登录用户、请求追踪 ID 等。典型应用场景在 Java Web 项目中一个 HTTP 请求通常会被 Tomcat 线程池中的某个工作线程处理。请求处理流程大致如下用户请求 ↓ Tomcat 分配工作线程 ↓ Filter / Interceptor ↓ Controller ↓ Service ↓ DAO / Mapper如果在拦截器中解析出了当前登录用户并且希望后续的Controller、Service都能方便地获取用户信息而不是在每个方法中层层传参就可以使用ThreadLocal保存当前用户。由于同一个请求通常会在同一个线程中执行所以后续业务代码可以从ThreadLocal中取到同一份用户信息。示例代码定义用户上下文工具类publicclassUserContext{privatestaticfinalThreadLocalUserDTOUSER_HOLDERnewThreadLocal();publicstaticvoidsetCurrentUser(UserDTOuser){USER_HOLDER.set(user);}publicstaticUserDTOgetCurrentUser(){returnUSER_HOLDER.get();}publicstaticvoidclear(){USER_HOLDER.remove();}}在拦截器中写入用户信息ComponentpublicclassLoginInterceptorimplementsHandlerInterceptor{OverridepublicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{Stringtokenrequest.getHeader(Authorization);// 假设这里已经根据 token 查询到了用户信息UserDTOuserDTOnewUserDTO();userDTO.setId(1001L);userDTO.setUsername(sihan);UserContext.setCurrentUser(userDTO);returntrue;}OverridepublicvoidafterCompletion(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler,Exceptionex)throwsException{UserContext.clear();}}在 Service 中读取用户信息ServicepublicclassOrderService{publicvoidcreateOrder(){UserDTOcurrentUserUserContext.getCurrentUser();LonguserIdcurrentUser.getId();// 根据当前用户创建订单}}注意事项Tomcat 使用线程池处理请求。线程在处理完一个请求后不会立即销毁而是会被复用去处理后续请求。因此如果使用ThreadLocal保存了用户信息却没有在请求结束后调用remove()清理数据可能会导致以下问题下一个请求错误读取到上一个请求的用户信息。长时间持有对象引用增加内存泄漏风险。所以在 Web 项目中使用ThreadLocal时一定要在请求结束阶段清理数据常见位置是拦截器的afterCompletion方法。