介绍
ThreadLocal是为实现对资源对象的线程隔离,使每个线程拥有自己的资源,避免并发时争用引发线程安全问题
实现原理: 主要是其内部存在一个ThreadLocalMap存储资源,将ThreadLocal对象自己为key,资源对象作为value,get方法获取,set存储,remove方法移除。
注:
- 添加时发生哈希冲突,使用开放寻址法解决,即从冲突位置向后寻找空闲位置插入
- ThreadLocalMap在垃圾回收时key是弱引用可以被回收但value是强引用,及时不再被引用也无法被垃圾回收,长期累加会导致内存泄露问题,所有在使用时应在对象使用完成后调用remove方法手动清除
使用场景
ThreadLocal可用来存储登录用户状态,即用户登录后将从前端请求获取的token解析出用户信息存储到ThreadLocal中,在业务层等使用时实现对用户状态的快速获取。请求结束时,调用remove方法清除
例子
ThreadLocal工具类
public class BaseContext {
private static ThreadLocal<MemberInfoDto> threadLocal = new ThreadLocal<>();
//作用在同一线程范围内
public static void setMember(MemberInfoDto memberLogin){
threadLocal.set(memberLogin);
}
public static MemberInfoDto getMember(){
return threadLocal.get();
}
public static void removeMember(){threadLocal.remove();}
}
在请求拦截器中获取token解析存入ThreadLocal
...
int status = AppJwtUtil.checkTokenTime(claims);
if (status == 1 || status == 2) {
BaseContext.setMember(new MemberInfoDto(
(Long) claims.get("id"),
(int) claims.get("flag")));
return true;
} else {
response.setStatus(401);
response.getWriter().print("无权限访问!");
return false;
}
...
在业务层获取使用
(通过ThreadLcoalMap存储的登录用户信息,判断该请求是否有权限调用该方法)
public ResponseResult getMemberList(String direction) {
if (BaseContext.getMember().getFlag() != 1) {
return ResponseResult.errorResult(500, "缺少权限");
}
List<MemberListDto> memberListDtos;
if (StrUtil.isBlank(direction)) {
memberListDtos = oaMemberMapper.getMemberList();
} else {
memberListDtos = oaMemberMapper.getMemberListWithDirection(direction);
}
return ResponseResult.okResult(memberListDtos);
}
请求完成时销毁
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
BaseContext.removeMember();
}