ThreadLocal介绍
ThreadLocal
是Java提供的一个类,它为每个线程提供了一个独立的变量副本。这意味着即使多个线程同时访问同一个ThreadLocal
实例,它们之间也不会相互干扰。这对于需要在线程生命周期内保持某些数据一致性的情况非常有用。处理并发请求的Web应用中,可以安全且高效地管理每个请求的上下文信息(如用户ID).
选择ThreadLocal原因
在多线程环境下,直接共享对象可能会导致数据竞争和不一致问题。而使用ThreadLocal
可以确保每个线程都有自己的变量副本,从而避免这些问题。特别是在Web应用程序中,每个请求通常由不同的线程处理,这时使用ThreadLocal
来存储与请求相关的上下文信息(例如用户ID)就显得特别合适。
实现步骤
定义ThreadLocal工具类
首先,我们需要定义一个包含ThreadLocal<Long>
变量的工具类,用于存储和检索用户ID。
public class BaseContext {
public static ThreadLocal<Long> threadLocal = new ThreadLocal<>();
public static void setCurrentId(Long id) {
threadLocal.set(id);
}
public static Long getCurrentId() {
return threadLocal.get();
}
public static void removeCurrentId() {
threadLocal.remove();
}
}
设置当前线程的用户ID
接下来,在请求到达时设置用户ID。这可以通过Spring MVC过滤器或拦截器实现。下面是一个简单的拦截器示例(在jwt令牌中校验中加入用户id):
try {
log.info("jwt校验:{}", token);
Claims claims = JwtUtil.parseJWT(jwtProperties.getAdminSecretKey(), token);
Long empId = Long.valueOf(claims.get(JwtClaimsConstant.EMP_ID).toString());
log.info("当前员工的id:{}", empId);
BaseContext.setCurrentId(empId);
//3、通过,放行
return true;
在业务逻辑中使用
现在,在业务逻辑层的任何地方都可以通过调用BaseContext.getCurrentId()
来获取当前线程关联的用户ID了。
Long empId = BaseContext.getCurrentId();
employeeMapper.insert(employee);
在serviceImpl层可以通过提供的工具类直接获取到用户ID。
注意事项与结论
清理ThreadLocal:务必记得在适当的时候清除ThreadLocal
变量,尤其是在Servlet容器重用线程的情况下,以避免潜在的内存泄漏。
线程安全性:虽然ThreadLocal
本身是线程安全的,因为它为每个线程提供了独立的变量副本,但如果你在复杂的并发环境中操作共享资源,则仍需注意同步问题。
通过使用ThreadLocal
,我们可以轻松地在线程生命周期内管理和访问特定于该线程的数据,比如用户ID。这种方法不仅提高了代码的可维护性,还增强了系统的性能和安全性。然而,正确地管理和清理ThreadLocal
变量同样重要,以避免内存泄漏等问题。