如何在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\) 间 ...
随机推荐
- RobotFramework+Selenium2环境搭建与入门实例
一.安装包 1.Python(推荐使用ActivePython,这个版本PATH已经配好了,也安了一些像pip这样的包) ActivePython-2.7.2.5-win32-x86.msi 2.Wx ...
- MediaCreationTool制作WIN10安装U盘,安装纯净版win10的通用教程
注意: 1.准备8G或8G以上U盘. 2.安装系统前备份好个人需要数据(制作U盘会格式化U盘,U盘内有需要的数据也事先备份好) 3.有预装office的务必记住自己激活office账户和密码以免重装后 ...
- Deepin-linux下的linux的终端下软件安装和卸载方法
1.方法一: sudo apt update #最好第一步是它 sudo apt install <package_name> --no-upgrade #安装该package但是不升级. ...
- iOS 上通过 802.11k、802.11r 和 802.11v 实现 Wi-Fi 网络漫游
在 iOS 上通过 802.11k.802.11r 和 802.11v 实现 Wi-Fi 网络漫游 了解 iOS 如何使用 Wi-Fi 网络标准提升客户端漫游性能. iOS 支持在企业级 Wi-Fi ...
- 虚拟交换系统-VSS
1.虚拟交换系统VSS技术概述 VSS的特点: VSS将两台Catalyst 6500/4500系列交换机组合为单一虚拟交换机,对外来看,只有一台交换机,管理冗余链路如同管理自己的一个单一接口. VS ...
- Educational Codeforces Round 80 C. Two Arrays(组合数快速取模)
You are given two integers nn and mm . Calculate the number of pairs of arrays (a,b)(a,b) such that: ...
- js屏幕上下滚动条
<!doctype html> <html> <head> <meta charset="utf-8"> <title> ...
- PHP实现链式操作的三种方法详解
这篇文章主要介绍了PHP实现链式操作的三种方法,结合实例形式分析了php链式操作的相关实现技巧与使用注意事项,需要的朋友可以参考下 本文实例讲述了PHP实现链式操作的三种方法.分享给大家供大家参考,具 ...
- 「题解」「美团 CodeM 资格赛」跳格子
目录 「题解」「美团 CodeM 资格赛」跳格子 题目描述 考场思路 思路分析及正解代码 「题解」「美团 CodeM 资格赛」跳格子 今天真的考自闭了... \(T1\) 花了 \(2h\) 都没有搞 ...
- 如何搭建OWASP测试靶机
刚刚入门的新手都需要一个可以用来练习的环境,但是dvwa的搭建需要相关环境,所以这里推荐大家在虚拟机上搭建owasp靶机,里面集成了dvwa靶机. https://sourceforge.net/pr ...