• 接口间参数传递的一种实现方式


    本文的主题是ThreadLocal,作者将其用于接口自动化框架设计,并解决用例并发执行带来的线程不安全问题。

    首先介绍ThreadLocal的原理与使用,其次介绍使用场景以及接口自动化的简单演示。

    一、解析ThreadLocal

    ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的,也就是说该变量是当前线程独有的变量。ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量,可以称为线程本地变量。
    查看ThreadLocal实现源码,会发现有四个方法比较重要。

    public T get()
    public void set(T value)
    protected T initialValue()
    public void remove()
    
    • 1
    • 2
    • 3
    • 4

    下面通过实例给大家演示下ThreadLocal的原理与用法。

    set就是设置值,get就是获取值,如果没有值,返回null,看上去,ThreadLocal就是一个单一对象的容器,比如:

    public class ThreadLocalBasic {
    
        static ThreadLocal local = new ThreadLocal<>();
    
        public static void main(String[] args) throws InterruptedException{
            // main 线程,设置local值为100
            local.set(100);
            Thread child = new Thread(){
                // Thread-0 线程
                @Override
                public void run(){
                    System.out.println(Thread.currentThread().getName() + ": "+ local.get());
                    local.set(200);
                    System.out.println(Thread.currentThread().getName() + ": "+local.get());
                }
            };
            child.start();
            child.join();
            System.out.println(Thread.currentThread().getName()+ ": " + local.get());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    图片

    通过结果发现,main线程对local变量的设置对Thread-0线程不起作用,Thread-0线程对local变量的改变也不会影响main线程,它们访问的虽然是同一个变量local,但每个线程都有自己的独立的值,这就是线程本地变量的含义。

    initialValue用于提供初始值,可以通过匿名内部类的方式提供,当调用get方法时,如果之前没有设置过,会调用该方法获取初始值,默认实现是返回null。remove删掉当前线程对应的值,如果删掉后,再次调用get,会再调用initialValue获取初始值。

    public class ThreadLocalInit {
        static ThreadLocal local = new ThreadLocal(){
            @Override
            protected Integer initialValue(){
                return 100;
            }
        };
        public static void main(String[] args) {
            System.out.println(local.get());
            local.set(200);
            local.remove();
            System.out.println(local.get());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    图片

    二、ThreadLocal使用场景

    日期处理

    ThreadLocal是实现线程安全的一种方案,比如对于DateFormat/SimpleDateFormat,每个线程使用自己的DateFormat,就不存在安全问题了,在线程的整个使用过程中,只需要创建一次,又避免了频繁创建的开销。

    public class ThreadLocalDateFormat {
        static ThreadLocal sdf = new ThreadLocal() {
            @Override
            protected DateFormat initialValue() {
                return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        }
        };
        public static String date2String(Date date){
            return sdf.get().format(date);
        }
        public static Date string2Date(String str) throws ParseException{
            return sdf.get().parse(str);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    三、接口自动化上下文信息

    ThreadLocal的典型用途是提供上下文信息,比如在一个Web服务器中,一个线程执行用户的请求,在执行过程中,很多代码都会访问一些共同的信息,比如请求信息、用户身份信息、数据库连接、当前事务等,它们是线程执行过程中的全局信息,如果作为参数在不同代码间传递,使用ThreadLocal就很方便,这样又在并发的场景下保证了线程安全。

    下面以登录接口,测试查询接口为例,登录后直接将token存入ThreadLocal的token中,然后在queryTest使用token传递给queryBook接口用于鉴权。

    public class ThreadLocalDemoTest {
    
        static ThreadLocal token = new ThreadLocal();
    
        // 模拟登陆,将token set到上下文
        @BeforeTest
        public static void login(){
            token.set(LoginService.login("admin"));
        }
    
        @Test
        public void queryTest(){
            String response = QueryBookService.queryBook(token.get());
            Assert.assertEquals(response, "管理员查询图书");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    当然了,上述例子比较简单,线程本地变量仅存储token;如果需要存储更多的信息,那该咋办?

    我们可以对ThreadLocal进行封装成ThreadLocalUtil,开发一个用于存储map的put和取map的getContextMap方法。

    private final static ThreadLocal> THREAD_CONTEXT = new MapThreadLocal();
    
    public static void put(String key, Object value) {
            getContextMap().put(key, value);
    }
    static Map getContextMap() {
            return THREAD_CONTEXT.get();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    用例实践:

    public class ThreadLocalTest {
    
        // 模拟登陆,将token set到上下文
        @BeforeTest
        public static void login(){
            ThreadLocalUtil.put("token", LoginService.login("admin"));
            ThreadLocalUtil.put("username", "软件质量保障");
        }
    
        @Test
        public void queryTest(){
            String response = QueryBookService.queryBook(ThreadLocalUtil.getContextMap().get("token").toString());
            Assert.assertEquals(response, "管理员查询图书");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    现在我邀请你进入我们的软件测试学习交流群:746506216】,备注“入群”, 大家可以一起探讨交流软件测试,共同学习软件测试技术、面试等软件测试方方面面,还会有免费直播课,收获更多测试技巧,我们一起进阶Python自动化测试/测试开发,走向高薪之路。

    喜欢软件测试的小伙伴们,如果我的博客对你有帮助、如果你喜欢我的博客内容,请 “点赞” “评论” “收藏” 一 键三连哦!

    在这里插入图片描述

    在这里插入图片描述

  • 相关阅读:
    git 笔记 常用命令reset merge rebase
    java计算机毕业设计ssm养老管理系统-敬老院系统
    【智慧工地源码】物联网和传感器技术在智慧工地的应用
    搜索——最短路模型,多源bfs
    韩国coupang需要懂韩文吗?平台入驻条件及费用?——站斧浏览器
    OSI参考模型个人总结
    Centos7 DNS 服务器配置步骤
    Nginx 从入门到精通-Nginx-Web服务器的瑞士军刀
    nginx自定义负载均衡及根据cpu运行自定义负载均衡
    【高级网络程序设计】Mid-Term Test
  • 原文地址:https://blog.csdn.net/m0_67695717/article/details/126879346