ThreadLocal理解

thread类中有:ThreadLocal.ThreadLocalMap threadLocals = null;

 ThreadLocal理解_第1张图片

ThreagLocal的方法

public void set(T value) {
    Thread t = Thread.currentThread();  //获取当前线程
    ThreadLocalMap map = getMap(t);     //获取threadLocals ,就是上面的那个对象
    if (map != null)                    //第一次以外设置就不为空,把当前的ThreadLocal对象作为key value作为值存入,也可以说是更新值
        map.set(this, value);           
    else
        createMap(t, value);//第一次设置就是为空的,执行方法看下面
}
第一次设置时执行的,创建一个新的ThreadLocalMap,并且把当前对象也就是 声明的ThreadLocal对象  private static final ThreadLocal tl = new ThreadLocal<>();作为key,set的值作为value存进去。
void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}
get方法
public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}
static class ThreadLocalMap {

    /**
     * The entries in this hash map extend WeakReference, using
     * its main ref field as the key (which is always a
     * ThreadLocal object).  Note that null keys (i.e. entry.get()
     * == null) mean that the key is no longer referenced, so the
     * entry can be expunged from table.  Such entries are referred to
     * as "stale entries" in the code that follows.
     */
    static class Entry extends WeakReference> {
        /** The value associated with this ThreadLocal. */
        Object value;

        Entry(ThreadLocal k, Object v) {
            super(k);
            value = v;
        }
    }

ThreadLocal理解_第2张图片

thread的ThreadLocal.ThreadLocalMap threadLocals(这个是threadlocal的内部类)会把每一个threadlocal对象作为key。一个threadlocal只能对应一个值,thread的ThreadLocal.ThreadLocalMap threadLocals中可以存储多个threadlocal (也就是一个线程可以有多个threadlocal对象)  也就是下图这样子。可能解释的不太清楚,还有就是threadlocal本身不保存数据,都是保存在thread的ThreadLocal.ThreadLocalMap threadLocals上的,获取和设置值的时候threadlocal通过方法获取当前线程设置/获取以自己为key的对应的值

ThreadLocal理解_第3张图片

thread和request对比


Web容器中有三个周期 request/Httpsession/application
其中request是客户端发出的一个请求,这个request的载体就是一个 线程,实际等同于一个线程的生命周期。

Request是封装在线程上面一个抽象概念。

而ThreadLocal则相当于 多个线程的一个共享全局变量存储地,它里面保存的是和每个线程相关的状态。所以threadLocal是为线程服务的,和线程处于一个底层位置。

当一个线程或request结束时,threadlocal中的状态就没有了,所以threadLocal基本 类似request.setAttribute作用,threadLocal中的对象状态的生命周期等同于request.

使用ThreadLocal可以大量减少参数的传递(),可以 使代码简洁。。。。

实战:登陆鉴权

登陆拦截器

public class LoginInterceptor extends HandlerInterceptorAdapter {

    private JwtProperties jwtProperties;

    // 定义一个线程域,存放登录用户
    private static final ThreadLocal tl = new ThreadLocal<>();

    public LoginInterceptor(JwtProperties jwtProperties) {
        this.jwtProperties = jwtProperties;
    }

//preHandle,处理器执行前调用
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 查询token
        String token = CookieUtils.getCookieValue(request, "LY_TOKEN");
        if (StringUtils.isBlank(token)) {
            // 未登录,返回401
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            return false;
        }
        // 有token,查询用户信息
        try {
            // 解析成功,证明已经登录
            UserInfo user = JwtUtils.getInfoFromToken(token, jwtProperties.getPublicKey());
            // 放入线程域
            tl.set(user);
            return true;
        } catch (Exception e) {
            // 抛出异常,证明未登录或超时,返回401
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            return false;
        }

    }


//afterCompletioncontroller执行结束后执行,释放
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        tl.remove();
    }

//获取当前线程下的threadlocal变量副本
    public static UserInfo getLoginUser() { 
        return tl.get();
    }
}
配置拦截器
@Configuration
@EnableConfigurationProperties(JwtProperties.class)
public class MvcConfig implements WebMvcConfigurer {

    @Autowired
    private JwtProperties jwtProperties;

//注入拦截器到容器
    @Bean
    public LoginInterceptor loginInterceptor() {
        return new LoginInterceptor(jwtProperties);
    }

注册拦截器,并添加拦截路径
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor())  
                .addPathPatterns("/order/**");
    }
}
service层中获取对应的值
UserInfo user = LoginInterceptor.getLoginUser();

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(ThreadLocal理解)