如何在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字段,以确保线程始终看到当前值,如此处解释。你可以从下载所有⽰例的源代码。
现在,让我们⾸先看看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 = String();
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"));
}
}
}
该测试从两个线程并⾏执⾏两个⽅法。为了测试所有线程交织,我们使⽤来⾃第7⾏的类,将完整的测
试放在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,
}
}
public String toString() {
AddressValue local = addressValue;
return "street=" + Street()
+ ",city=" +    City() +
",phoneNumber=" + PhoneNumber();
}
}
AddressUsingCopyOnWrite每次更新变量时,该类都会创建⼀个值  addressValue。这样可以确保我们始终读取⼀致的地址,⽆论是更新前后的值。
如果我们使⽤这两个类运⾏测试,则测试成功。
我们需要测试什么?
到⽬前为⽌,我们测试了的组合toString和  update线程安全性。为了测试⼀个类是否是线程安全的,我们需要测试所有修改⽅法组合以及只读⽅法与修改⽅法的所有组合。因此,对于我们的⽰例类,我们需要测试以下两种组合:
spring street是什么意思1. update 和 update
2. toString 和 update
由于只读⽅法的组合是⾃动线程安全的,因此我们不需要测试toString⽅法与其⾃⾝的组合。
数据竞赛
到⽬前为⽌,我们使⽤易失性字段来避免数据争⽤。让我们看看当我们使⽤普通字段时会发⽣什么。因此,在我们的线程安全类中
AddressUsingCopyOnWrite,我们删除了volatile修饰符并重新运⾏测试。现在,vmlens在⽂件target / interleave / issues.html中报告数据争⽤
数据争⽤是对线程可能读取陈旧值的字段的访问。如果线程确实读取了⼀个过时的值,则取决于外部因素,例如编译器正在使⽤的优
化,JVM正在运⾏的硬件体系结构以及线程正在哪个内核上运⾏。为了使始终能够检测到与那些外部因素⽆关的数据竞争,vmlens在测试运⾏的执⾏跟踪中搜索数据竞争。如果vmlens在⽰例中到了⼀个,它将在问题报告中报告它们。
摘要
线程安全性测试与典型的单线程测试不同。要测试两种⽅法$$ anonymous $$ nd b的组合是否是线程安全的,请从两个不同的线程中调⽤它们。放在⼀个while循环迭代完整的测试了从类帮助所有线程的交错  AllInterleavings来⾃。测试结果是b之后还是a之后b。为了测试⼀个类是否是线程安全的,请测试修改⽅法的所有组合以及只读⽅法和修改⽅法的所有组合。
> 喜欢这篇⽂章的可以点个赞,欢迎⼤家留⾔评论,记得关注我,每天持续更新技术⼲货、职场趣事、海量⾯试资料等等
> 如果你对java技术很感兴趣也可以交流学习,共同学习进步。
> 不要再⽤"没有时间“来掩饰⾃⼰思想上的懒惰!趁年轻,使劲拼,给未来的⾃⼰⼀个交代
⽂章写道这⾥,欢迎完善交流。最后奉上近期整理出来的⼀套完整的java架构思维导图,分享给⼤家对照知识点参考学习。有更多JVM、Mysql、Tomcat、Spring Boot、Spring Cloud、Zookeeper、Kafka、RabbitMQ、RockerMQ、Redis、ELK、Git等Java⼲货

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。