码神之路项目之ThreadLocal线程变量与内存泄露

142 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 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时。