通过优锐课的java核心笔记中,我们可以看到关于如何在java中测试类是否线程安全的一些知识点汇总,分享给大家学习参考。

线程安全性测试与典型的单线程测试不同。为了测试一个方法是否是线程安全的,我们需要从多个线程中并行调用该方法。我们需要对所有潜在的线程交织进行此操作。然后,我们需要检查结果是否正确。

这三个测试要求导致了一种特殊的线程安全测试,该测试不同于典型的单线程测试。由于我们要测试所有线程交错,因此我们的测试必须是可重复的并自动运行。而且由于这些方法并行运行,因此潜在的结果是不同结果的组合。

让我们看一个例子,看看实际情况。

测试线程安全

假设我们要测试表示地址的以下类是否是线程安全的。它提供一种更新街道和城市的方法,一种方法更新以及一种读取完整地址的方法,该方法是   toString:

public class MutableAddress {

    private volatile String street;

    private volatile String city;

    private volatile String phoneNumber;

    public MutableAddress(String street, String city,

        String phoneNumber) {

        this.street = street;

        this.city = city;

        this.phoneNumber = phoneNumber;

    }

    public void update(String street ,String city ) {

        this.street = street;

        this.city = city;

    }

    public String toString() {

        return "street=" + street + ",city=" + city + ",

        phoneNumber=" + phoneNumber;

    }

}

  

我在第2行到第4行中使用了volatile字段,以确保线程始终看到当前值,如此处更详细地解释。你可以从GitHub下载所有示例的源代码。

现在,让我们首先看看toStringand和update 的组合   是否是线程安全的。这是测试:

import com.vmlens.api.AllInterleavings;

public class TestToStringAndUpdate {

    @Test

    public void testMutableAddress() throws InterruptedException {

        try (AllInterleavings allInterleavings =

            new AllInterleavings("TestToStringAndUpdate_Not_Thread_Safe");) {

            while (allInterleavings.hasNext()) {

                MutableAddress address = new MutableAddress("E. Bonanza St.",

                     "South Park", "456 77 99");

                String readAddress = null;

                Thread first = new Thread(() -> {

                    address.update("Evergreen Terrace", "Springfield");

                });

                first.start();

                readAddress = address.toString();

                first.join();

                assertTrue("readAddress:" + readAddress,readAddress.equals(

        "street=E. Bonanza St.,city=South Park,phoneNumber=456 77 99")

                    || readAddress.equals(

        "street=Evergreen Terrace,city=Springfield,phoneNumber=456 77 99"));

            }

        }

}

  

该测试从两个线程并行执行两个方法。为了测试所有线程交织,我们使用来自vmlens的第7行的AllInterleavings类,将完整的测试放在while循环中,对所有线程交织进行迭代。要查看该类是否线程安全,我们将结果与潜在结果进行比较,更新前和更新后第17至20行的值。

运行测试会导致以下错误:

java.lang.AssertionError: readAddress:street=Evergreen Terrace

        ,city=South Park,phoneNumber=456 77 99

    at com.vmlens.tutorialCopyOnWrite.TestToStringAndUpdate.

        testMutableAddress(TestToStringAndUpdate.java:22)

  

问题在于,对于其中一个线程与线程ID为30的线程进行交织,首先更新街道名称,然后主线程(线程ID 1)读取街道和城市名称。因此,主线程读取了导致错误的部分更新地址。

为了使地址类具有线程安全性,我们在每次更新地址时都会复制地址值。这是使用此技术的线程安全实现。它由两个类组成:一个不变值和一个可变容器。

首先,不变值类:

public class AddressValue {

    private final String street;

    private final String city;

    private final String phoneNumber;

    public AddressValue(String street, String city,

                String phoneNumber) {

        super();

        this.street = street;

        this.city = city;

        this.phoneNumber = phoneNumber;

    }

    public String getStreet() {

        return street;

    }

    public String getCity() {

        return city;

    }

    public String getPhoneNumber() {

        return phoneNumber;

    }

}

  

其次是可变容器类:

public class AddressUsingCopyOnWrite {

    private volatile AddressValue addressValue;

    private final Object LOCK = new Object();

    public AddressUsingCopyOnWrite(String street,

            String city, String phone) {

        this.addressValue = new AddressValue( street,

                city,  phone);

    }

    public void update(String street ,String city ) {

        synchronized(LOCK){

            addressValue = new AddressValue(  street,  city,  

                    addressValue.getPhoneNumber() );

        }

    }

    public String toString() {

        AddressValue local = addressValue;

        return "street=" + local.getStreet()

        + ",city=" +    local.getCity() +

        ",phoneNumber=" + local.getPhoneNumber();

    }

}

  

AddressUsingCopyOnWrite每次更新变量时,该类   都会创建一个新的地址值   addressValue。这样可以确保我们始终读取一致的地址,无论是更新前后的值。

如果我们使用这两个类运行测试,则测试成功。

我们需要测试什么?

到目前为止,我们测试了的组合toString和   update线程安全性。为了测试一个类是否是线程安全的,我们需要测试所有修改方法组合以及只读方法与修改方法的所有组合。因此,对于我们的示例类,我们需要测试以下两种组合:

  1. update 和 update
  2. toString 和 update

由于只读方法的组合是自动线程安全的,因此我们不需要测试toString方法与其自身的组合。

数据竞赛

到目前为止,我们使用易失性字段来避免数据争用。让我们看看当我们使用普通字段时会发生什么。因此,在我们的线程安全类中   AddressUsingCopyOnWrite,我们删除了volatile修饰符并重新运行测试。现在,vmlens在文件target / interleave / issues.html中报告数据争用

数据争用是对线程可能读取陈旧值的字段的访问。如果线程确实读取了一个过时的值,则取决于外部因素,例如编译器正在使用的优化,JVM正在运行的硬件体系结构以及线程正在哪个内核上运行。为了使始终能够检测到与那些外部因素无关的数据竞争,vmlens在测试运行的执行跟踪中搜索数据竞争。如果vmlens在示例中找到了一个,它将在问题报告中报告它们。

摘要

线程安全性测试与典型的单线程测试不同。要测试两种方法$$ anonymous $$ nd b的组合是否是线程安全的,请从两个不同的线程中调用它们。放在一个while循环迭代完整的测试了从类帮助所有线程的交错  AllInterleavings来自vmlens。测试结果是b之后还是a之后b。为了测试一个类是否是线程安全的,请测试修改方法的所有组合以及只读方法和修改方法的所有组合。

> 喜欢这篇文章的可以点个赞,欢迎大家留言评论,记得关注我,每天持续更新技术干货、职场趣事、海量面试资料等等
 > 如果你对java技术很感兴趣也可以交流学习,共同学习进步。 
> 不要再用"没有时间“来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代

文章写道这里,欢迎完善交流。最后奉上近期整理出来的一套完整的java架构思维导图,分享给大家对照知识点参考学习。有更多JVM、Mysql、Tomcat、Spring Boot、Spring Cloud、Zookeeper、Kafka、RabbitMQ、RockerMQ、Redis、ELK、Git等Java干货

如何在Java中测试类是否是线程安全的的更多相关文章

  1. Java多线程有哪几种实现方式? Java中的类如何保证线程安全? 请说明ThreadLocal的用法和适用场景

    java的同步机制,大概是通过:1.synchronized:2.Object方法中的wait,notify:3.ThreadLocal机制来实现的, 其中synchronized有两种用法:1.对类 ...

  2. 如何在Java中调用Python代码

    有时候,我们会碰到这样的问题:与A同学合作写代码,A同学只会写Python,而不会Java, 而你只会写Java并不擅长Python,并且发现难以用Java来重写对方的代码,这时,就不得不想方设法“调 ...

  3. 第四节:详细讲解Java中的类和面向对象思想

    前言 大家好,给大家带来详细讲解Java中的类和面向对象思想的概述,希望你们喜欢 类和面向对象 在Java中怎样理解对象,创建对象和引用:什么是引用,对于基础学习的同学,要深入了解引用.示例:Stri ...

  4. Java中Optional类的使用

    从 Java 8 引入的一个很有趣的特性是 Optional  类.Optional 类主要解决的问题是臭名昭著的空指针异常(NullPointerException) —— 每个 Java 程序员都 ...

  5. Java中主类中定义方法加static和不加static的区别

     Java中主类中定义方法加static和不加static的区别(前者可以省略类名直接在主方法调用(类名.方法),后者必须先实例化后用实例调用) 知识点:1.Getter and Setter 的应用 ...

  6. java中File类的常用方法总结

    java中File类的常用方法 创建: createNewFile()在指定的路径创建一个空文件,成功返回true,如果已经存在就不创建,然后返回false. mkdir() 在指定的位置创建一个此抽 ...

  7. Java中的类反射

    一.反射的概念 : 反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问.检测和修改它本身状态或行为的一种能力.这一概念的提出很快引发了计算机科学领域关于应用反射性的研究.它首先被程序 ...

  8. 用代码说话:如何在Java中实现线程

    并发编程是Java语言的重要特性之一,"如何在Java中实现线程"是学习并发编程的入门知识,也是Java工程师面试必备的基础知识.本文从线程说起,然后用代码说明如何在Java中实现 ...

  9. 如何在 Java 中实现 Dijkstra 最短路算法

    定义 最短路问题的定义为:设 \(G=(V,E)\) 为连通图,图中各边 \((v_i,v_j)\) 有权 \(l_{ij}\) (\(l_{ij}=\infty\) 表示 \(v_i,v_j\) 间 ...

随机推荐

  1. 手把手教你使用Hexo+GitHub搭建自己的个人博客网站

    安装nodejs环境 这个直接搜索安装即可,安装完成之后,通过如下命令检测环境变量是否安装成功: λ node -v # 输出版本号 v12.13.1 正确输入版本号即可. 安装cnpm cnpm是淘 ...

  2. 影响IPSec的网络问题

    影响IPSec VPN的网络问题:①.动态地址问题:两个 站点之间IPSec VPN的条件是站点之间有固定的IP地址,假如说分支站点采用ADSL上网链路,那么其IP地址是动态的,那么就在VPN时出现问 ...

  3. 常用SQL语句大全

    一些常用SQL语句大全   一.基础1.说明:创建数据库CREATE DATABASE database-name2.说明:删除数据库drop database dbname3.说明:备份sql se ...

  4. 洛谷P2758编辑距离(线性DP)

    题目描述 设A和B是两个字符串.我们要用最少的字符操作次数,将字符串A转换为字符串B.这里所说的字符操作共有三种: 1.删除一个字符: 2.插入一个字符: 3.将一个字符改为另一个字符: !皆为小写字 ...

  5. 【剑指Offer面试编程题】题目1509:树中两个结点的最低公共祖先--九度OJ

    题目描述: 给定一棵树,同时给出树中的两个结点,求它们的最低公共祖先. 输入: 输入可能包含多个测试样例. 对于每个测试案例,输入的第一行为一个数n(0<n<1000),代表测试样例的个数 ...

  6. ES-elasticsearch安装-linux

    (1)安装JDK(ES是使用java开发的) (2)安装ES(虚拟机内存大于一个g) 1)创建普通用户启动 2)非常占用内存(默认1个g的内存) (3)创建一个普通用户(用于启动ES) groupad ...

  7. Website's Game source code

    A Darkroom by doublespeakgames <!DOCTYPE html> <html itemscope itemtype="https://schem ...

  8. 修改html内联样式的方法

    问题:如下图弹出页面操作不了 分析:审查元素,发现是内联元素样式z-index:19891015导致的,修改内联元素样式z-index:0发现可以操作了 解决方法:内联样式优先级高,再引入css覆盖样 ...

  9. Vagrant 安装使用

    先安装虚拟机 https://www.virtualbox.org/ 再安装 https://www.vagrantup.com/  1.nginxhttp://nginx.org/download/ ...

  10. 在 ubuntu 中安装python虚拟环境

    直接看命令一路操作(注:python3 下): 1.安装虚拟环境: sudo pip3 install virtualenv 2.安装虚拟环境扩展管理工具: sudo pip3 install vir ...