一.什么是ThreadLocal?
ThreadLocal
,即线程本地变量。如果你创建了一个 ThreadLocal
变量,那么访问这个变量的每个线程都会有这个变量的一个本地拷贝,多个线程操作这个变量的时候,实际是在操作自己本地内存里面的变量,从而起到线程隔离的作用,也就是两个线程间互不影响,避免了并发场景下的线程安全问题。
这样就有利于我们使用ThreadLocal来存取数据。
上图灰色以及绿色是两个不同的线程,我们可以看出两个线程间的数据是独立互不影响,而利用这个ThreadLocal我们可以实现两个事情:
- 通过单线程保存数据来减少参数传递
- 在同一线程间共享数据
二.ThreadLocal的使用:
这里我们以将JWT令牌放入线程内并在Service层读取JWT令牌并获取内部用户 id 值为例 =>
1.导入ThreadLocal的工具类:
这里工具类是new一个ThreadLocal来维护全局唯一的ThreadLocal对象,然后使用其内部的三个方法就可以使用了 =>
public class ThreadLocalUtil {
//提供ThreadLocal对象,
private static final ThreadLocal THREAD_LOCAL = new ThreadLocal();
//根据键获取值
public static <T> T get(){
return (T) THREAD_LOCAL.get();
}
//存储键值对
public static void set(Object value){
THREAD_LOCAL.set(value);
}
//清除ThreadLocal 防止内存泄漏
public static void remove(){
THREAD_LOCAL.remove();
}
}
这里需要注意,因为ThreadLocal生命周期非常长,所以我们在使用完后需要调用内部方法 remove() 删除 ThreadLocal 以此来避免内存泄露。
2.在拦截器内把JWT令牌存储到ThreadLocal中:
①当拦截器拦截请求,我们就会再preHandle()方法内开辟线程空间并保存JWT令牌。
②这里注意当响应完数据,也就是请求结束,我们就不会使用数据,而这个时候我们就需要移除数据,这个时候我们重写拦截器内部的afterCompletion()方法,在方法内调用remove()方法删除ThreadLocal。
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request , HttpServletResponse response , Object handle) throws Exception{
String token = request.getHeader("Authorization"); //Authorization为请求头的名字
try {
//令牌验证
Map<String,Object> claims = JwtUtil.parseToken(token);
//线程开辟空间存储
ThreadLocalUtil.set(claims);
//放行
return true;
}catch (Exception e){
//http响应状态码为401在·
response.setStatus(401);
//不放行
return false;
}
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//清空线程数据
ThreadLocalUtil.remove();
}
}
3.使用ThreadLocal来获取存储数据使用:
@Service
public class CategoryServiceImpl implements CategoryService {
@Autowired
private CategoryMapper categoryMapper;
@Override
public void add(Category category) {
category.setCreateTime(LocalDateTime.now());
category.setUpdateTime(LocalDateTime.now());
//在ThreadLocal中获取
Map<String,Object> map = ThreadLocalUtil.get();
Integer userId = (Integer) map.get("id");
category.setCreateUser(userId);
categoryMapper.add(category);
}
}
好了,ThreadLocal的使用就到这里了,感谢收看!!!