ThreadLocal讲解与代码实践
原创一、概念
ThreadLocal是线程变量,即存款。ThreadLocal中的变量仅属于 当前线程唯一的变量,与其他线程隔离 ,ThreadLocal为每个线程创建一个副本以存储变量。
因为每个 Thread 有自己的实例副本,另一个 Thread 那么,无法访问 在多个线程之间不存在共享问题。 它与普通变量的不同之处在于,使用该变量的每个线程初始化实例的一个完全独立的副本。
2.适用场景
ThreadLocal变量适用于线程在方法或类之间隔离和共享的场景。
例如,当用户未登录时,需要为购物网站中的购物车功能提供一个临时购物车。id,对应于添加到购物车的项目。用户登录后,需要该用户。id和临时id整合购物车。此时,在进入购物车相关方法之前,需要在拦截器中判断用户是否已登录,并登录用户。id放入ThreadLocal,传递给相关Controller和Service方法使用。未登录就生成临时id。
3.实际代码
1.编写拦截器以识别用户是否已登录。
密钥代码:
-
public static ThreadLocal threadLocal = new ThreadLocal<>();
-
threadLocal.set(userInfoTo);
-
threadLocal.get();
-
(如果没有定义public static,或者使用线程池,您需要使用该调用。remove();要删除的方法)
/**
- @author guanghaocheng
- @version 1.0
- 翅膀被灰尘和雾气稍稍补充。,蜡烛末端的光线增加了太阳和月亮的辉光。
- @date 2022/4/6 19:31
- 在执行目标方法之前确定用户的登录状态,并将传递封装到controller目标请求 */
public class CartInterceptor implements HandlerInterceptor {
public static ThreadLocal
threadLocal = new ThreadLocal<>(); //在目标方法执行之前拦截 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { UserInfoTo userInfoTo = new UserInfoTo(); HttpSession session = request.getSession(); MemberRespVo member = (MemberRespVo) session.getAttribute(AuthServerConstant.LOGIN_USER); if(member != null){ //用户已登录 userInfoTo.setUserId(member.getId()); } Cookie[] cookies = request.getCookies(); if(cookies!=null && cookies.length>0){ for (Cookie cookie : cookies) { String cookieName = cookie.getName(); if(Objects.equals(cookieName,CartConstant.TEMP_USER_COOKIE_NAME)){ userInfoTo.setUserKey(cookie.getValue()); } } } if (StringUtils.isEmpty(userInfoTo.getUserKey())){ //这意味着没有临时用户,无论是否登录,临时用户都必须存在。 String uuid = UUID.randomUUID().toString(); userInfoTo.setUserKey(uuid); } //在执行目标方法之前,将数据放入。ThreadLocal在中,线程共享数据。 threadLocal.set(userInfoTo); return true;//所有释放,只要你来到计量方法所有释放 } //执行目标方法后:将临时用户分配给要保存的浏览器。 @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { UserInfoTo userInfoTo = threadLocal.get(); Cookie cookie = new Cookie(CartConstant.TEMP_USER_COOKIE_NAME, userInfoTo.getUserKey()); cookie.setDomain("***.com"); cookie.setMaxAge(CartConstant.TEMP_USER_COOKIE_TIMEOUT); response.addCookie(cookie); } }
2.注册拦截器.
@Configuration
public class mallWebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new CartInterceptor()).addPathPatterns("/**");
}
}
三、在controller直接从ThreadLocal数据。
/**
* @author guanghaocheng
* @version 1.0
* 翅膀被灰尘和雾气稍稍补充。,蜡烛末端的光线增加了太阳和月亮的辉光。
* @date 2022/4/6 19:09
*
*
* 登录:有session
* 无登录:根据cookie中带的user-key执行临时用户标识
* 第一次:如果没有临时用户,请帮助创建临时用户。
* 如果您稍后登录,临时身份仍然存在,购物车应该与登录用户的购物车合并。
*/
@Controller
public class CartController {
@GetMapping("/cart.html")
public String cartListPage(HttpSession session){
//从拦截器获取用户信息。
UserInfoTo userInfoTo = CartInterceptor.threadLocal.get();
System.out.println(userInfoTo.toString());
return "cartList";
}
4.不能只在Controller中用,在Service如也可直接使用。只要是同一条线,你就可以得到它。
@Service
@Slf4j
public class CartServiceImpl implements CartService {
@Autowired
private StringRedisTemplate redisTemplate;
private final String CART_PERFIX = "mall:cart";
@Override
public CartItem addToCart(Long skuId, Integer num) {
//因为拦截器配置的执行。Controller在所有拦截程序之前,因此在请求中Controller还是Service可以直接获取threadlocal
UserInfoTo userInfoTo = CartInterceptor.threadLocal.get();
String cartkey = "";
if(userInfoTo.getUserKey() != null){
cartkey = CART_PERFIX + userInfoTo.getUserId();
}else{
cartkey = CART_PERFIX + userInfoTo.getUserId();
}
return null;
}
}
4.注意事项
我们定义ThreadLocal通常使用变量。private static装饰因为当线程结束时 private static修饰ThreadLocal 相对实例副本可以自动回收。进入上面的例子。
如果没用private static修饰,您需要手动呼叫。remove();方法,清除该变量,否则将导致。 内存泄漏 。
ThreadLocal 的set方法维护ThreadLocalMap,实际上 ThreadLocalMap 中使用的 key 为 ThreadLocal 弱引用、弱引用的特点是,如果这个对象中只存在弱引用,那么它肯定会在下一次垃圾收集中被清除。
所以如果 ThreadLocal 如果没有外部强引用,它将在垃圾收集期间被清理,因此 ThreadLocalMap将此用于 ThreadLocal 的 key 它也将被清理。但是value 它是一个强大的参考,不会被清理,所以它会出现。 key 为 null 的 value。
ThreadLocal事实上,它是一个绑定到线程的变量,因此存在一个问题:如果没有ThreadLocal删除中的变量(remove或者替换,它的生命周期将与线程共存。通常,线程池中的线程管理使用线程重用。线程很难在线程池中结束甚至永不结束,这意味着线程的持续时间将是不可预测的,甚至是JVM生命周期是一致的。例如,如果ThreadLocal集合类或复杂对象每次都直接或间接地包装在同一个集合中。ThreadLocal在取出对象然后对内容进行操作之后,内部集合类和复杂对象所占用的空间可能会开始不断扩展。
版权声明
所有资源都来源于爬虫采集,如有侵权请联系我们,我们将立即删除