一、优势特点
a、ThreadLocal中包含的对象,可以在不同的Thread中保持不同的副本,且该副本只能当前Thread使用
b、解决了多线程之间的共享问题
c、使用于每个线程都需要自己独立的实例,并且该实例需要在多个方法中使用,也即变量在线程见隔离,而在方法或者类间共享的场景。
d、达到线程安全的目的
e、不需要加锁,执行效率高
f、更加节省内存,节省开销
g、免去传参的繁琐,降低代码耦合度
......
二、主要使用场景
在每个线程内需要保存全局变量(例如在拦截器中获取用户信息),让不同方法直接使用,避免参数传递的麻烦
三、使用事例
3.1 创建UserThreadLocal.java
public class UserThreadLocal {
private static ThreadLocal<UserInfoPo> threadLocal = new ThreadLocal<>();
/**
* 设置线程数据UserInfoPo
* @param UserInfoPo 缓存数据
*/
public static void set(UserInfoPo user){
threadLocal.set(user);
}
/**
* 获取线程数据
* @return UserInfoPo
*/
public static UserInfoPo get(){
return threadLocal.get();
}
/**
* 删除线程数据
*/
public static void remove(){
threadLocal.remove();
}
}
3.2 实现HandlerInterceptor拦截器
@Slf4j
@Component
public class UserInterceptor implements HandlerInterceptor {
private final RedisTemplate redisTemplate;
@Autowired
public UserInterceptor(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String userToken = request.getHeader("token");
if (StringUtils.isEmpty(userToken)) {
throw new UserException(ResultCode.USER_NOT_EXCEPTION);
return false;
}
Object userInfoPo = redisTemplate.opsForValue().get(userToken);
if (StringUtils.isEmpty(userInfoPo)) {
throw new UserException(ResultCode.USER_NOT_EXCEPTION);
return false;
}
UserThreadLocal.set((UserInfoPo) userInfoPo);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
UserThreadLocal.remove();
}
}
3.3 InterceptorMvc.java的配置
@Configuration
public class InterceptorMvc implements WebMvcConfigurer {
private final UserInterceptor userInterceptor;
@Autowired
public InterceptorMvc(UserInterceptor userInterceptor) {
this.userInterceptor = userInterceptor;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 定义路由拦截数组
String[] userPathPattern = {"/test/**"};
// 除去特殊拦截路径
String[] userExcludePathPatterns = {"/test/testOne"};
registry.addInterceptor(userInterceptor).addPathPatterns(userPathPattern).excludePathPatterns(userExcludePathPatterns);
}
}
3.4 controller层相关拦截接口直接使用获取对象
UserInfoPo userInfoPo = UserThreadLocal.get();
四、ThreadLocal注意点
4.1 内存泄漏
某个对象不会再被使用,但是该对象的内存却无法被收回
强引用:当内存不足时触发GC,宁愿抛出OOM也不会回收强引用的内存
弱引用:触发GC后便会回收弱引用的内存
原因: 线程一直再执行中,强引用链下的value值始终无法被回收导致内存溢出
参考链接: https://zhuanlan.zhihu.com/p/128102523
参考地址:http://www.threadlocal.cn/#1