开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第12天,点击查看活动详情
本文主要讲解码神之路项目中ThreadLocal相关内容。
ThreadLocal线程变量与内存泄露
ThreadLocal是一个以ThreadLocal对象为键、任意对象为值的存储结构。ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的。ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。 ThreadLocal中Key被设计成了弱引用。当线程结束了,会直接回收key,但是我们的value依然存在,这就造成了内存泄漏。为了规避此风险,我们需要在线程结束前,去remove。
#本地线程变量ThreadLocal
#ThreadLocal是什么
ThreadLocal是一个以ThreadLocal对象为键、任意对象为值的存储结构。ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的。ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。
作为一个面试常问的点,使用场景那也是相当的丰富:
1、在进行对象跨层传递的时候,使用ThreadLocal可以避免多次传递,打破层次间的约束。
2、线程间数据隔离
3、进行事务操作,用于存储线程事务信息。
4、数据库连接,Session会话管理。
#马神之路项目中怎么用ThreadLocal
项目中,使用ThreadLocal去保存用户的登录信息,在请求的线程之内,可以随时获取登录的用户。项目中,将需要登录的业务做了一个拦截器 (opens new window)拦截,在执行控制层方法前,先执行拦截器preHandle方法,在该方法内我们进行登录验证,并将验证好的用户放入UserThreadLocal中。放行的位置执行即可:
UserThreadLocal.put(sysUser);
放入线程用set方法,取信息用get,删除用remove。项目中对这三个方法进行二次封装,代码如下:
public class UserThreadLocal {
private UserThreadLocal(){}
//线程变量隔离
private static final ThreadLocal<SysUser> LOCAL = new ThreadLocal<>();
public static void put(SysUser sysUser){
LOCAL.set(sysUser);
}
public static SysUser get(){
return LOCAL.get();
}
public static void remove(){
LOCAL.remove();
}
}
当我们需要用到登录的用户时,直接get出来:SysUser sysUser = UserThreadLocal.get();
#ThreadLocal内存泄露
ThreadLocal中Key被设计成了弱引用。当线程结束了,会直接回收key,但是我们的value依然存在,这就造成了内存泄漏。为了规避此风险,我们需要在线程结束前,去remove()。在项目中,我们在拦截器中设置了remove,拦截器重写了了afterCompletion方法,该方法在controller方法执行之后执行,执行在postHandle返回为true时。