用ThreadLocal来优化下代码吧
最近接手了一个老项目,看到一个很有意思的现象。
这个项目中大量的方法入参都会带上user信息,比如这样
它的意图是希望在方法内使用user的信息,但是如此大范围的传递用户信息,第一感觉就是不优雅。那有什么办法可以优化一下呢?
我们第一反应是,可以存一个全局变量,在初始位置将用户信息存入全局变量,然后在需要的地方去get一下。
那在WEB应用中,每个请求都是一个独立线程,怎么去标记呢?
可以用线程的id去作为map的key,将该请求的用户信息作为map的value。
咦?很熟悉的感觉。
没错,Java已经帮我们封装好了这么一个对象,它就是我们今天要说的ThreadLocal。
- 什么是ThreadLocal
- 如何使用ThreadLocal优化userid层层传递的问题
- ThreadLocal原理是啥
- ThreadLocal的其他使用场景
1.什么是ThreadLocal
先来看下JDK的注释:
简单翻译过来,就是说:
ThreadLocal提供了线程隔离的局部变量,通过get( )和set( )方法操作当前线程对应的变量,而且不会和其他线程冲突,实现了基于线程的数据隔离。
2.如何使用ThreadLocal进行优化
话不多说,基于我们开头的例子,我迫不及待地用ThreadLocal来优化一下。
2.1 构建基于ThreadLocal的上下文
定义一个SessionUser类,存储用户信息,包括用户id、用户名。
然后定义一个基于ThreadLocal的上下文SessionUserContext,代码如下所示。
2.2 信息存入ThreadLocal中
在我们的优化案例中,就是存入用户信息。
解析请求中的用户信息有很多方法。本文以HandlerIntercept为例,说明下MVC中的一种方式。
- 实现HandlerIntercept接口
- 重写preHandler方法
- 解析HttpServletRequest,获取用户信息
- 用户信息存于SessionUserContext
源码如下所示。
2.3 在需要的地方获取信息
原本需要传入CurrentUser的参数都可以去掉了。
在需要用户信息的时候,直接从SessionUserContext中获取即可。
哈哈,是不是看起来一下子清爽了很多。
可以在任何地方获取user信息,不再需要层层传递用户信息了。
3.ThreadLocal实现原理
上面我们已经知道了怎么通过ThreadLocal进行优化。
下面,我们要 知其然知其所以然,一起看看ThreadLocal实现原理吧。
3.1 set方法
Set方法应该是ThreadLocal的核心逻辑了。
主要三步:
- 获取当前线程
- 获取ThreadLocalMap对象
- 如果ThreadLocalMap对象存在,则将当前线程对象作为key,要存储的对象作为value存到map中 如果ThreadLocalMap对象不存在,就调用creatMap( )进行创建
3.2 ThreadLocalMap是什么。
ThreadLocalMap是一个定义在ThreadLocal类内部的静态类,里面还定义了一个Entry类作为存储值的地方。
ThreadLocalMap的key是当前ThreadLocal对象,value是我们要存储的值(对象)。
调用creatMap的时候,就是新建一个ThreadLocalMap对象
同时,ThreadLocalMap在Thread类中作为一个属性存在。
每个线程Thread维护了ThreadLocalMap这么一个Map,这个map的key是LocalThread对象本身,value则是要存储的对象
3.3 get方法
Get方法就比较简单了,就是从map中取值的过程。
3.4 ThreadLocal小结
现在,让我们重新梳理一遍,看看ThreadLocal是如何实现变量的线程隔离的:
- 每个Thread维护着一个ThreadLocalMap的引用
- ThreadLocalMap是ThreadLocal的内部类,用Entry来进行存储,key是ThreadLocal对象,值是传递进来的对象
- 调用ThreadLocal的get()/set()方法时,实际上就是以ThreadLocal对象为key,在ThreadLocalMap中读写value
4.实战要点
在一开始的优化设计中,不知道大家有没有注意到对ThreadLocal的remove调用。
这里就需要谈谈ThreadLocal使用时的,两个要点。尤其是在使用线程池的时候使用ThreadLocal。
4.1 避免内存泄露
在ThreadLocalMap介绍的时候,我们可以看到,ThreadLocalMap是Thread的一个属性。因此,ThreadLocalMap和Thread的生命周期是一样的。
如果没有手动删除对应的ThreadLocal的key,那么就会造成内存泄漏无法回收。尤其在线程池环境下,线程会被不断复用。
4.2 线程池避免重复线程变量影响
以前文优化案例为例。
在MVC中,每次请求进来会使用线程池复用线程。如果请求带了用户信息,那么就会重置ThreadLocal对应的用户信息,如果请求没有带用户信息,必须手动清除一下当前ThreadLocal对应的变量,否则后面使用过程中可能会造成混乱。
都看到最后了,原创不易,点个关注,点个赞吧~
文章持续更新,可以微信搜索「阿丸笔记 」第一时间阅读,回复关键字【学习】有我准备的一线大厂面试资料。
知识碎片重新梳理,构建Java知识图谱:github.com/saigu/JavaK…(历史文章查阅非常方便)
用ThreadLocal来优化下代码吧的更多相关文章
- 优化PHP代码的40条建议(转载)
[size=5][color=Red](译文)优化PHP代码的40条建议[/color][/size] 40 Tips for optimizing your php Code 原文地址:http:/ ...
- 网站静态化处理—web前端优化—下【终篇】(13)
网站静态化处理—web前端优化—下[终篇](13) 本篇继续web前端优化的讨论,开始我先讲个我所知道的一个故事,有家大型的企业顺应时代发展的潮流开始投身于互联网行业了,它们为此专门设立了一个事业部, ...
- 如何优化JAVA代码
通过使用一些辅助性工具来找到程序中的瓶颈,然后就可以对瓶颈部分的代码进行优化.一般有两种方案:即优化代码或更改设计方法.我们一般会选择后者,因为不去调用以下代码要比调用一些优化的代码更能提高程序的性能 ...
- PostgreSQL 优化器代码概览
简介 PostgreSQL 的开发源自上世纪80年代,它最初是 Michael Stonebraker 等人在美国国防部支持下创建的POSTGRE项目.上世纪末,Andrew Yu 等人在它上面搭建了 ...
- 优化Python代码的4种方法
介绍 作为数据科学家,编写优化的Python代码非常非常重要.杂乱,效率低下的代码即浪费你的时间甚至浪费你项目的钱.经验丰富的数据科学家和专业人员都知道,当我们与客户合作时,杂乱的代码是不可接受的. ...
- (原创)一步步优化业务代码之——从数据库获取DataTable并绑定到List<Class>
一,前言 现实业务当中,有一个很常见的流程:从数据库获取数据到DataTable,然后将DataTable绑定到实体类集合上,一般是List<Class>,代码写起来也简单:遍历+赋值就可 ...
- Android Studio环境下代码混淆+签名打包
Android Studio环境下代码混淆+签名打包 作者 Mr_冯先生 关注 2016.08.21 01:10 字数 1040 阅读 734评论 5喜欢 34 注:本文使用的Android Stud ...
- 电脑打不开网页,使用dns优化下就可以了。
通过电脑管家dns优化下就可以了.启用114DNS
- Linux编译Windows共享目录下代码
Linux编译Windows共享目录下代码(金庆的专栏)万神服务器代码是跨平台的.平时策划在Windows上开自己的服务器测试,测试和发布服务器为Linux.开发时,先在Windows上编译测试,再到 ...
随机推荐
- 焦大:SEO重思录(上)收录量和收录率的重新定位
http://www.wocaoseo.com/thread-198-1-1.html 前一段焦大在seo前线就看到有人问为何我收录量很大但是流量很低呢?有时候几百万的收录量但是流量却只有1000不到 ...
- spring如何创建RESTful Service
REST REST,是指REpresentational State Transfer,有个精辟的解释什么是RESTful, 看url就知道要什么 看method就知道干什么 看status code ...
- 为什么 list(range) 比 [i for i in range()] 快?
为什么 list(range) 比 [i for i in range()] 快? t0 = time.time() list(range(100000)) print(time.time()-t0) ...
- Unity CommandLine
CommandLineArguments https://docs.unity3d.com/Manual/CommandLineArguments.html Unity3D游戏开发之“unity3D命 ...
- 【C#】Random类中构造方法、时间种子与随机数序列的关系
Random类 构造函数 1) Random random = new Random(); // 无参数构造函数使用系统时钟生成其种子值 然而,系统时钟取值范围有限,因此在小规模计算中,可能无法使用不 ...
- nginx的gzip压缩
随着nginx的发展,越来越多的网站使用nginx,因此nginx的优化变得越来越重要,今天我们来看看nginx的gzip压缩到底是怎么压缩的呢? gzip(GNU-ZIP)是一种压缩技术.经过gzi ...
- EasyPoi导出Excel
这几天一直在忙工作中的事情,在工作中有一个问题,可能是因为刚开始接触这个EasyPoi,对其也没有太多的理解,在项目中就使用了,有一个需求,是要导出项目中所有的表格,今天就对这个需求进行分析和实现吧; ...
- jackson读取json tree讲解
待读取的json文本: {"data":{"count":4031,"list":[{"symbol":"SH ...
- ubuntu安装docker-ce 、docker-ce-cli、containerd.io
问题 ubuntu安装docker的时候特别慢,百度搜了一大堆都没讲到点子上,最后请教了大佬才知道是源的问题 安装 修改源 sudo gedit /etc/apt/sources.list 添加源 阿 ...
- Java 后端开发常用的 10 种第三方服务
请肆无忌惮地点赞吧,微信搜索[沉默王二]关注这个在九朝古都洛阳苟且偷生的程序员.本文 GitHub github.com/itwanger 已收录,里面还有我精心为你准备的一线大厂面试题. 严格意义上 ...