如何在Java中测试类是否是线程安全的
通过优锐课的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线程安全性。为了测试一个类是否是线程安全的,我们需要测试所有修改方法组合以及只读方法与修改方法的所有组合。因此,对于我们的示例类,我们需要测试以下两种组合:
- update 和 update
- 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中测试类是否是线程安全的的更多相关文章
- Java多线程有哪几种实现方式? Java中的类如何保证线程安全? 请说明ThreadLocal的用法和适用场景
java的同步机制,大概是通过:1.synchronized:2.Object方法中的wait,notify:3.ThreadLocal机制来实现的, 其中synchronized有两种用法:1.对类 ...
- 如何在Java中调用Python代码
有时候,我们会碰到这样的问题:与A同学合作写代码,A同学只会写Python,而不会Java, 而你只会写Java并不擅长Python,并且发现难以用Java来重写对方的代码,这时,就不得不想方设法“调 ...
- 第四节:详细讲解Java中的类和面向对象思想
前言 大家好,给大家带来详细讲解Java中的类和面向对象思想的概述,希望你们喜欢 类和面向对象 在Java中怎样理解对象,创建对象和引用:什么是引用,对于基础学习的同学,要深入了解引用.示例:Stri ...
- Java中Optional类的使用
从 Java 8 引入的一个很有趣的特性是 Optional 类.Optional 类主要解决的问题是臭名昭著的空指针异常(NullPointerException) —— 每个 Java 程序员都 ...
- Java中主类中定义方法加static和不加static的区别
Java中主类中定义方法加static和不加static的区别(前者可以省略类名直接在主方法调用(类名.方法),后者必须先实例化后用实例调用) 知识点:1.Getter and Setter 的应用 ...
- java中File类的常用方法总结
java中File类的常用方法 创建: createNewFile()在指定的路径创建一个空文件,成功返回true,如果已经存在就不创建,然后返回false. mkdir() 在指定的位置创建一个此抽 ...
- Java中的类反射
一.反射的概念 : 反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问.检测和修改它本身状态或行为的一种能力.这一概念的提出很快引发了计算机科学领域关于应用反射性的研究.它首先被程序 ...
- 用代码说话:如何在Java中实现线程
并发编程是Java语言的重要特性之一,"如何在Java中实现线程"是学习并发编程的入门知识,也是Java工程师面试必备的基础知识.本文从线程说起,然后用代码说明如何在Java中实现 ...
- 如何在 Java 中实现 Dijkstra 最短路算法
定义 最短路问题的定义为:设 \(G=(V,E)\) 为连通图,图中各边 \((v_i,v_j)\) 有权 \(l_{ij}\) (\(l_{ij}=\infty\) 表示 \(v_i,v_j\) 间 ...
随机推荐
- Hive的安装与基础指令
一.Hive安装 Hive的安装相对比较简单,Hive是基于Hadoop来使用的,所以搭建Hadoop伪分布式或完全分布式即可,Hive安装过程如下: ①安装并启动Hadoop 在博主的其他博客中有安 ...
- [Unity] Shader Graph Error 当前渲染管道与此主节点不兼容(The current render pipeline is not compatible with this master node)
Shader Graph Error : The current render pipeline is not compatible with this master node 问题产生环境: Un ...
- 实现纸牌游戏的随机抽牌洗牌过程(item系列几个内置方法的实例)
实现纸牌游戏的随机抽牌洗牌过程(item系列几个内置方法的实例) 1.namedtuple:命名元组,可以创建一个没有方法只有属性的类 from collections import namedtup ...
- 遍历pd.Series的index和value的方法
以下内容来自链接:https://blog.csdn.net/qq_42648305/article/details/89634186 遍历pd.Series的index和value的方法如下,pyt ...
- VS误删sln项目文件怎么办
以项目名为Test为例 打开Test/Test目录下的 Test.vcxproj 文件,试着运行一下,退出后提示保存sln文件,选择一个目录即可.
- GO测试
测试 Go拥有一个轻量级的测试框架,它由 go test 命令和 testing 包构成. 你可以通过创建一个名字以 _test.go 结尾的,包含名为 TestXXX 且签名为 func (t *t ...
- 洛谷P1198 [JSOI2008]最大数(线段树)
题目描述 现在请求你维护一个数列,要求提供以下两种操作: 1. 查询操作. 语法:Q L 功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值. 限制:LLL不超过当前数列的长度.(L> ...
- 解决苹果手机(IOS)input失焦后,页面不恢复的问题
var winHeight = $(window).height(); var u = navigator.userAgent, app = navigator.appVersionvar isIOS ...
- 微信授权登陆接入第三方App(步骤总结)Android。
这几天开发要用到微信授权的功能,所以就研究了一下.可是微信开放平台接入指南里有几个地方写的不清不楚.在此总结一下,以便需要的人. 很多微信公众平台的应用如果移植到app上的话就需要微信授权登陆了. 目 ...
- linux#自启动脚本
编写脚本: /etc/init.d/myscriptname # chkconfig: # description: 描述信息,描述信息,上面的90表示在众多开机启动脚本的优先级,10表示在众多关机启 ...