用ThreadLocal来优化下代码吧

开发
今天给大家介绍用ThreadLocal来优化下代码

                            [[344746]]

 最近接手了一个老项目,看到一个很有意思的现象。

这个项目中大量的方法入参都会带上user信息,比如这样

它的意图是希望在方法内使用user的信息,但是如此大范围的传递用户信息,第一感觉就是不优雅。那有什么办法可以优化一下呢?

我们第一反应是,可以存一个全局变量,在初始位置将用户信息存入全局变量,然后在需要的地方去get一下。

那在WEB应用中,每个请求都是一个独立线程,怎么去标记呢?

可以用线程的id去作为map的key,将该请求的用户信息作为map的value。

没错,Java已经帮我们封装好了这么一个对象,它就是我们今天要说的ThreadLocal。

  • 什么是ThreadLocal
  • 如何使用ThreadLocal优化userid层层传递的问题
  • ThreadLocal原理是啥
  • ThreadLocal的实战要点

1.什么是ThreadLocal
先来看下JDK的注释:

简单翻译过来,就是说:

  1. 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对应的变量,否则后面使用过程中可能会造成混乱。

 

责任编辑:姜华 来源: 阿丸笔记
相关推荐

2016-11-22 16:34:11

githubgit前端

2011-12-26 09:34:26

2020-05-20 09:47:57

程序员代码网络

2020-12-07 05:50:54

print()Python代码

2019-12-17 08:45:30

ifelseJava

2010-03-04 16:33:21

Ubuntu cvs

2024-05-11 18:48:40

技巧代码技能

2020-12-04 07:49:54

AICtrl C代码

2023-01-05 13:36:41

Script优化任务

2019-09-12 08:38:15

机器学习电影数据库

2021-01-15 05:16:37

Vue3开源代码量

2018-02-02 10:12:57

谷歌开源 TensorFlow

2020-01-22 16:46:04

Python 开发编程语言

2024-10-28 08:15:32

2010-04-16 10:42:10

Oracle存储过程

2021-05-26 08:02:03

ThreadLocal多线程多线程并发安全

2022-05-09 07:27:50

ThreadLocaJava

2019-08-30 20:50:19

戴尔

2018-05-28 07:27:18

2022-04-11 00:06:19

开发服务架构
点赞
收藏

51CTO技术栈公众号