zoukankan      html  css  js  c++  java
  • 宇智波程序笔记47-用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小结

    xmlns:xsi="http://www.lanboyulezc.cn /2001/XMLSchema-instance"
      
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://www.dongdongrji.cn maven.apache.org/xsd/maven-4.0.0.xsd">
      
      <artifactId>dubbo<www.xxinyoupt.cn /artifactId>
      
      <groupId>nacos</groupId>
      
      <version>0.0.1-SNAPSHOT<www.lanboyulezc.cn /version>
      
      <priority value=www.lanboylgw.com/"info"www.chuanchenpt.cn/ />
      
      <appender-ref ref=www.yixingxzc.cn"stdout"www.lanboylgw.com />
      
      <appender-ref ref="fileout"www.baichuangyule.cn /
      
      换掉encoder的实现类或者换掉layout的实现类就可以了
      
      <?xml version= www.lanboyulezc.cn www.jiuerylzc.cn"1.0"www.zhuyngyule.cn encoding=www.bhylzc.cn"UTF-8"?>
      
      <configuration debug=www.shicaiyulezc.cn"false"www.huachenguoj.com >
      
      <property name=www.51huayuzc.cn"APP_NAME" value="logtest"/>
      
      <property name=www.xinhuihpw.com "LOG_HOME" value="./logs" www.wanyaylezc.cn//>
      
      <appender name="STDOUT" class="www".yachengyl.cn"ch.qos.logback.core.ConsoleAppender">
      
      <!--替换成AspectLogbackEncoder-->
      
      <encoder class="www.shengrenyp.cn "com.yomahub.tlog.core.enhance.logback.AspectLogbackEncoder"www.51huayuzc.cn>
      
      <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{www.pinguo2yl.com} - %msg%n</pattern>
      
      <appender www.baishenjzc.cn name="FILE" www.baihua178.cn class="ch.qos.logback.core.rolling.RollingFileAppender">
      
      <File>${LOG_HOME}/${APP_www.jinliyld.cn NAME}.log<www.baihua178.cn /File>
      
      <rollingPolicy class="www.jintianxuesha.com"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"www.wanyaylezc.cn/>
      
      <FileNamePattern>www.yachengyl.cn ${LOG_HOME}/${APP_NAME}.log.%d{yyyy-MM-dd}.%i.log</FileNamePattern>
      
      <MaxHistory>30<www.51huayuzc.cn /MaxHistory>
      
      <maxFileSize>1000MB<www.jinliyld.cn /maxFileSize>

    现在,让我们重新梳理一遍,看看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对应的变量,否则后面使用过程中可能会造成混乱。

  • 相关阅读:
    Codeforces 1491 D. Zookeeper and The Infinite Zoo (二进制处理)
    Codeforces 1479A. Searching Local Minimum(注意输入+二分)
    Codeforces 1480B. The Great Hero(阅读模拟题,注意数据范围和攻击顺序)
    Codeforces 1480A. Yet Another String Game (阅读理解题)
    windows 10 开启全盘瞬间索引功能
    JetBrains CLion C++ IDE连接wsl2(Ubuntu)时,报错"Unable to establish SSL connection"解决方案
    WorkFlowy 的 MarkDown 预览方案解决
    git 学习 完全学懂
    jeecgboot <j-popup
    面试之类加载器
  • 原文地址:https://www.cnblogs.com/woshixiaowang/p/13757846.html
Copyright © 2011-2022 走看看