最近接手了一个老项目,看到一个颇有意思的现象。git
这个项目中大量的方法入参都会带上user信息,好比这样github
它的意图是但愿在方法内使用user的信息,可是如此大范围的传递用户信息,第一感受就是不优雅。那有什么办法能够优化一下呢?面试
咱们第一反应是,能够存一个全局变量,在初始位置将用户信息存入全局变量,而后在须要的地方去get一下。微信
那在WEB应用中,每一个请求都是一个独立线程,怎么去标记呢?学习
能够用线程的id去做为map的key,将该请求的用户信息做为map的value。优化
咦?很熟悉的感受。线程
没错,Java已经帮咱们封装好了这么一个对象,它就是咱们今天要说的ThreadLocal。翻译
先来看下JDK的注释:设计
简单翻译过来,就是说:3d
ThreadLocal提供了线程隔离的局部变量,经过get( )和set( )方法操做当前线程对应的变量,并且不会和其余线程冲突,实现了基于线程的数据隔离。
话很少说,基于咱们开头的例子,我火烧眉毛地用ThreadLocal来优化一下。
定义一个SessionUser类,存储用户信息,包括用户id、用户名。
而后定义一个基于ThreadLocal的上下文SessionUserContext,代码以下所示。
在咱们的优化案例中,就是存入用户信息。
解析请求中的用户信息有不少方法。本文以HandlerIntercept为例,说明下MVC中的一种方式。
源码以下所示。
本来须要传入CurrentUser的参数均可以去掉了。
在须要用户信息的时候,直接从SessionUserContext中获取便可。
哈哈,是否是看起来一会儿清爽了不少。
能够在任何地方获取user信息,再也不须要层层传递用户信息了。
上面咱们已经知道了怎么经过ThreadLocal进行优化。
下面,咱们要 知其然知其因此然,一块儿看看ThreadLocal实现原理吧。
Set方法应该是ThreadLocal的核心逻辑了。
主要三步:
ThreadLocalMap是一个定义在ThreadLocal类内部的静态类,里面还定义了一个Entry类做为存储值的地方。
ThreadLocalMap的key是当前ThreadLocal对象,value是咱们要存储的值(对象)。
调用creatMap的时候,就是新建一个ThreadLocalMap对象
同时,ThreadLocalMap在Thread类中做为一个属性存在。
每一个线程Thread维护了ThreadLocalMap这么一个Map,这个map的key是LocalThread对象自己,value则是要存储的对象
Get方法就比较简单了,就是从map中取值的过程。
如今,让咱们从新梳理一遍,看看ThreadLocal是如何实现变量的线程隔离的:
在一开始的优化设计中,不知道你们有没有注意到对ThreadLocal的remove调用。
这里就须要谈谈ThreadLocal使用时的,两个要点。尤为是在使用线程池的时候使用ThreadLocal。
在ThreadLocalMap介绍的时候,咱们能够看到,ThreadLocalMap是Thread的一个属性。所以,ThreadLocalMap和Thread的生命周期是同样的。
若是没有手动删除对应的ThreadLocal的key,那么就会形成内存泄漏没法回收。尤为在线程池环境下,线程会被不断复用。
之前文优化案例为例。
在MVC中,每次请求进来会使用线程池复用线程。若是请求带了用户信息,那么就会重置ThreadLocal对应的用户信息,若是请求没有带用户信息,必须手动清除一下当前ThreadLocal对应的变量,不然后面使用过程当中可能会形成混乱。
都看到最后了,原创不易,点个关注,点个赞吧~
文章持续更新,能够微信搜索「阿丸笔记 」第一时间阅读,回复关键字【学习】有我准备的一线大厂面试资料。
知识碎片从新梳理,构建Java知识图谱: github.com/saigu/JavaK…(历史文章查阅很是方便)