如何在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\) 间 ...
随机推荐
- Maven学习笔记:POM标签大全详解
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/20 ...
- 【渗透测试】MS17-010 "永恒之蓝" 修复方案
多所院校电脑被勒索,吾等当代大学生怎能坐视不管. --------------------------------------------------------------------------- ...
- twisted reactor 实现源码解析
twisted reactor 实现源码解析 1. reactor源码解析 1.1. 案例分析代码: from twisted.internet import protocol fro ...
- tkinter学习(1)
1.hit_me的一个简单tk窗口学习1.1 代码: import tkinter as tk win = tk.Tk() win.title('my first window') #定义标题,如果未 ...
- javascript 原型继承 与class extends 继承对比
//父类 Animal function Animal (name) { this.name = name; this.sleep = function () { console.log(this ...
- 杭电 2013 猴子吃桃 递归解法&循环解法
题目估计看到过3次不止了,所以还是想复习下递归的运用. 奉上递归代码: #include <iostream> #include<math.h> #include <io ...
- 【HV】HVIL-High Voltage Interlock Loop
HVIL高压互锁功能 1.HVIL作用 High Voltage Interlock Loop 高压互锁,是在"ISO6469-3:电动汽车安全技术规范第三部分:人员电气伤害防护" ...
- 最全BT磁力搜索引擎索引(整理分享,每日更新)
btaa.xyz:http://www.veee.xyz/(可以访问,知名的BT磁力搜索,资源多,建议手机访问) 以下无法访问 idope.se:https://idope.se/(无法访问,资源丰富 ...
- IIS 配置迁移
使用管理员身份运行cmd 应用程序池: # 导出所有应用程序池 %windir%\system32\inetsrv\appcmd list apppool /config /xml > c:\a ...
- Linux搭建maven私服
1.把压缩包上传到服务器/usr/local/tmp 2.在/usr/local下创建nexus文件夹(mkdir nexus) 3.解压压缩包nexus-3.13.0-01-unix.tar.gz到 ...