• 0
  • 0
分享
  • ThreadLocal实践案例两则
  • FunTeste 2023-08-02 17:01:57 字数 2037 阅读 915 收藏 0

ThreadLocal是Java中的一个类,全路径:java.lang.ThreadLocal,用于在多线程环境下存储线程本地变量。在多线程应用程序中,不同线程之间共享数据可能会引发线程安全问题。

ThreadLocal通过为每个线程创建独立的变量副本,保证了线程间数据的隔离性,从而有效地解决了这一问题。线程之间的数据访问操作互不影响,提高了多线程应用程序的性能和可靠性。

ThreadLocal通常与线程池、异步任务和Web应用程序等场景结合使用,使得在多线程编程时更加方便和安全。

虽然ThreadLocal有这么多好处,但在之前的实际使用中用的并不多,只有在性能测试中的随机数性能问题探索和随机方法性能差异中有所使用。结论分享一下java.util.concurrent.ThreadLocalRandom这个性能最好。

在近期的测试实践中,又发现了一些有趣的应用场景,分享给大家。

场景一

我有一个工具类,用来去平台获取部分信息。伪代码如下:

package com.funtest.temp

import com.funtester.frame.SourceCode

class ThreadLocalTest extends SourceCode{

    /**
     * 获取域名
     * @return
     */
    static String getHost() {
        return EMPTY
    }

    /**
     * 获取信息
     * @return
     */
    static String getMsg() {
        return getHost() + "/funtester"
    }

    /**
     * 获取响应
     * @param url
     * @return
     */
    static String getRes(String url) {
        return EMPTY
    }

}

之所以设计getHost()这个方法原因是因为不同有不止一个地址所以写了这个方法(可忽略)。本来用来跑任务之后1个地址,所以getHost()只需要写死一个返回值即可。但是新需求来了,每次定时任务需要跑两个环境。

首先想到的思路就是在所有getMsg()方法里面都添加一个参数,用来标识请求的是哪个环境,这样做看起来比较简单,但是改动地方太多了。而且基于这些方法的脚本已经有不少一直在跑,一旦改动,发布之后还得重新修改脚本。

如果增加一个全局属性,那么在多个线程都在跑任务(假设会有交叉)的时候,这个全局属性就会被多个线程修改,导致线程不安全的问题。

然后我就想到了ThreadLocal,因为跑任务时候,我都是用线程池去跑,也就是所每个任务都有一个线程。这样我可以针对每个线程设置一个属性。每个线程修改当前线程持有的ThreadLocal属性又不会影响到其他线程。

真是瞌睡递枕头。

 static ThreadLocal<Boolean> Online = new ThreadLocal<Boolean>(){

        @Override
        protected Boolean initialValue() {
            return true
        }
    }
    

这样就可以在执行不同的环境时候,调用Online.set()方法进行修改了。

场景一

这是在Springboot开发过程中遇到的。原因跟以上有点像,但并不是static方法。简单描述一下:A接口参数aBean,B接口参数bBean,A接口实现中使用到了B接口的方法。以此为背景。

某次需求更新,需要在A接口增加一项功能,在对某个参数值扩充(这个值不会传递给bBean),针对新值在调用B接口方法的时候特殊处理。

这个时候我又想到了ThreadLocal,毕竟两件事情没隔几天。索性在A、B接口实现类中增加一个ThreadLocal的成员变量,A接口在获取到参数时,初始化这个变量。这样B接口就可以在处理时,获取变量,进行差别对待了。

虽然这个例子有点牵强,但在实践中确实简单高效的方式。如果改造aBean和bBean,势必会导致B接口出现多余字段(也可称为非必传字段),在我看来有点不能忍。

所以这虽然不是一个常见的解决方案,但在我的这种场景下,确实一个非常nice的方案。成本低,改动小,上线快。

当然也会给后续维护者造成一点点的困惑。这就是另一个故事了。

关于内存泄露

在看资料过程中,很多建议使用者规范使用ThreadLocal类,容易造成内存泄露。大家可以搜一搜看一看,还是很有必要的。

前面两个例子之所以没有使用规范,原因是因为在执行过程中,多次调用了Online.set()和Online.get()方法,会帮助JVM回收资源。不用额外调用Online.remove()主动释放内存。

  • 【留下美好印记】
    赞赏支持
登录 后发表评论
+ 关注

热门文章

    最新讲堂

      • 推荐阅读
      • 换一换
          • 读者提问:研发质量差,怎么办 ?阿常回答:这个问题乍一看,咱们作为测试好像管不着,那我们可以做些什么呢。一、作为测试无法保证质量测试人员的职责是在软件产品发布前,尽可能多地 、快速暴露有价值的缺陷,加速可交付质量的达成。测试人员虽然无法保证研发质量,但可以控制测试质量和测试进度,并且在测试结束后对于研发质量做出评价。一)控制测试质量和测试进度1、做好测试计划,准备好测试需要的工具和软件,好的规划是成功的一半。2、编写测试用例,最好进行一次用例评审,避免漏测重要的 case。3、开发提测前进行冒烟测试,冒烟测试通过后方可正式移交测试。4、及时汇报测试进度,用例执行情况、BUG 修复情况...
            0 0 736
            分享
          •   填一份51Testing行业调查问卷吧?内含2019-2022年的技术趋势和热点。点击下方链接,不仅能帮助你更了解测试行业,还能免费获得实战课程~链接:http://vote.51testing.com/  性能测试往往在投产上线前开展,无法对整个系统变更进行全面的覆盖测试,因此性能测试需求提出十分关键。性能测试需求交付过程中,需要对开发团队提出的测试需求进行审查,重点分析交付的测试需求是否充分覆盖了影响系统性能的因素,避免遗漏重要测试项,引发生产性能事件。  在很多企业中,性能测试需求交付都设置有需求评审环节,需求审查的动作也会包含系统变更影响性分析,其中最关键的分析内容就是梳理影响系统...
            0 0 932
            分享
          •   最近在利用JMeter做接口自动化测试工作,实现了很多场景的自动化,想着很多方法具有通用性,于是拿出来分享下,希望对大家有所启发。  今天分享的是场景是:JMeter进行接口测试,两种方法获取登录接口的Cookie值。  HTTPCookie管理器  在JMeter中,HTTPCookie管理器(HTTPCookieManager)用于管理发送和接收的HTTP请求中的Cookie。Cookie是服务器用来在客户端和服务器之间维持会话状态的一种机制,通过在请求和响应中传递Cookie来实现状态的保持。  这次分享的案例就是,在登录后,通过使用HTTPCookie管理器,可以自动处理和发送服务...
            0 0 1254
            分享
          • 在编写 Junit 测试时,我采用了同一套格式。如此,对于测试用例我就可以遵循这套格式进行编写和阅读,使所有的测试都整体划一。这种格式模板可以帮助我更快的编写测试,提高工作效率。今天我就与大家分享我的这套格式模板。(译者评:与我在测试建设原则中提出的继承原则相同,都是在进行一项测试工作之前,将公共的进行提取,统一格式模板,然后在以后的测试活动中都继承这套模板开展。)文件格式首先,在项目的测试包下新建一个测试文件/测试类,并且创建测试方法。在编写测试文件/测试类时,所有的测试文件/测试类都以 Test 结束,这样会容易理解其是一个测试文件/测试类,也方便后期维护时查看,编辑。例如一个名字为&nb...
            14 14 1814
            分享
          •        谈到浏览器兼容性测试,很多同学往往考虑到的都是如何覆盖全面,管它什么Chrome、FireFox、IE、Safari这种主流浏览器还是360、QQ、搜狗等一系列国产浏览器,都给它列到列表上边去,只要时间允许,就一个一个测起来。但是大家有没有想过,这个策略真的是正确的么?真的应该这样去进行测试么?       在回答上面的问题之前,我们先来思考这样一个问题:我们为什么要做兼容性测试? 或者换个说法,网站为什么会在不同的浏览器上展现出不同的样式?毫无疑问,其答案是因为内核不同、渲染方式不同。有了这样的结果...
            5 8 5686
            分享
      • 51testing软件测试圈微信