ThreadLocal是一个Java中的类,它提供了线程局部变量的支持。它可以用来在多线程环境下,为每个线程创建独立的变量副本,使每个线程都拥有自己的变量副本,互不影响。
使用ThreadLocal可以解决多线程之间共享变量的并发访问问题。它可以确保每个线程操作的变量是独立的,避免了线程安全问题的出现。通过ThreadLocal对象,每个线程在访问变量时都会得到自己的副本,修改副本也不会影响其他线程的副本。
ThreadLocal的常见用法是在多线程任务中保存和共享数据,例如在Web应用中保存用户会话信息、数据库连接、事务上下文等。每个线程都可以通过ThreadLocal对象获取到自己的独立副本,从而实现了线程安全的共享数据。
今天来说的相关应用是,在对用户进行登录操作后,如何得到当前登录用户的ID。在Web应用中,当用户登录后,通常会将用户的ID等信息保存在一个会话中,以便在整个会话期间进行访问。然而,在多线程环境下,不同的线程可能同时处理多个会话,所以需要一种机制来确保每个线程能够访问到自己的会话信息。
通过使用ThreadLocal,可以在用户登录时,将用户的ID保存在ThreadLocal中,这样在整个会话期间,每个线程都可以通过ThreadLocal获取自己独立的用户ID副本。
1.创建BaseContext类,用来调用ThreadLocal底层的三个方法:
package com.sky.context;
public class BaseContext {
public static ThreadLocal<Long> threadLocal = new ThreadLocal<>();
/**
* 设置
* @param id
*/
public static void setCurrentId(Long id) {
threadLocal.set(id);
}
/**
* 获取
* @return
*/
public static Long getCurrentId() {
return threadLocal.get();
}
/**
* 删除
*/
public static void removeCurrentId() {
threadLocal.remove();
}
}
2.在拦截器类中,加入set()和remove()方法:
/**
* 校验jwt
* 在Handler之前执行。就是Controller中标记了@XxxMapping注解的方式
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//判断当前拦截到的是Controller的方法还是其他资源
if (!(handler instanceof HandlerMethod)) {
//当前拦截到的不是动态方法,直接放行
return true;
}
//1、从请求头中获取令牌
String token = request.getHeader(jwtProperties.getAdminTokenName());
//2、校验令牌
try {
log.info("jwt校验:{}", token);
Claims claims = JwtUtil.parseJWT(jwtProperties.getAdminSecretKey(), token);
//当前登录的员工ID
Long empId = Long.valueOf(claims.get(JwtClaimsConstant.EMP_ID).toString());
log.info("当前员工id:", empId);
//把登录ID设置到ThreadLocal上
BaseContext.setCurrentId(empId);
//3、通过,放行
return true;
} catch (Exception ex) {
//4、不通过,响应401状态码
response.setStatus(401);
return false;
}
}
/**
* 渲染视图之后执行。执行释放资源的操作
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//从ThreadLocal上删除登录ID
BaseContext.removeCurrentId();
}
3.通过以上方法,我们就可以在执行Controller方法中,通过BaseContext.getCurrentId()方法来获取到当前登录用户的ID。
我们在进行方法的执行过程中,会出现内存泄漏的情况。因此我们在ThreadLocal执行完后,使用了remove方法,就可以将ThreadLocal中存在的数据进行清理,有效地解决此问题。