写点什么

使用 Fray 检测 JVM 语言中的并发问题

  • 2025-12-11
    北京
  • 本文字数:2220 字

    阅读完需:约 7 分钟

大小:606.07K时长:03:26
使用Fray检测JVM语言中的并发问题

卡内基梅隆大学推出了Fray,这是一个面向 JVM 程序的并发测试工具,可以用于捕获和重放错误。Fray 是基于这篇研究论文用 Kotlin 编写的。它不能找出所有的并发问题,但能利用最新的研究成果最大化检测它们的机会。Fray 使用了影子锁定技术,通过额外的锁以特定顺序协调对共享资源的访问。

 

Fray 支持包括 JDK 25 在内的 Java 版本,并已成功发现JDK、Lucene、Kafka、Flink 和 Guava 中的 Bug。该框架能够检测多线程问题,但不能检测由并发内存写入引起的错误。Maven 需要以下插件和依赖配置:

 

<plugin>    <groupId>org.pastalab.fray.maven</groupId>    <artifactId>fray-plugins-maven</artifactId>    <version>0.6.9</version>    <executions>        <execution>            <id>prepare-fray</id>            <goals>                <goal>prepare-fray</goal>            </goals>        </execution>    </executions></plugin><dependency>    <groupId>org.pastalab.fray</groupId>    <artifactId>fray-junit</artifactId>    <version>0.6.9</version>    <scope>test</scope></dependency>
复制代码

 

或者,在 Gradle 中使用以下插件配置:

 

plugins {    id("org.pastalab.fray.gradle") version "0.6.9"}
复制代码

 

配置好 Gradle 后,可以使用以下命令运行测试:

 

./gradlew frayTest
复制代码

 

最后,可以使用 IntelliJ IDEA 运行 Fray 测试,具体方法请参考 GitHub 上提供的IDE文档

 

配置好构建系统后,可以使用 JUnit 5 运行测试,通过在类上使用注解 @ExtendWith(FrayTestExtension.class),在测试上使用注解 @ConcurrencyTest:

 

@ExtendWith(FrayTestExtension.class)public class MyFirstTest {    @ConcurrencyTest    public void myTest() {    }}
复制代码

 

下面的 BankAccount 类是一个简化的示例,当多个线程访问同步代码时可能会导致死锁:

 

public class BankAccount {    public BankAccount(double balance) {        this.balance = balance;    }    private double balance;    public void transfer(double amount, BankAccount toAccount) {        synchronized (this) {            synchronized (toAccount) {                this.balance -= amount;                toAccount.balance += amount;            }        }    }}
复制代码

 

为了检测死锁,我们创建了一个 Fray 测试,并显式设置迭代次数为 10。要了解完整的参数集,可以查看 GitHub 上的ConcurrencyTest.kt文件。

 

@ExtendWith(FrayTestExtension.class)public class BankAccountTest {    public void myBankAccountTest() throws InterruptedException {        BankAccount bankAccount1 = new BankAccount(5000);        BankAccount bankAccount2 = new BankAccount(6000);        Thread thread1 = new Thread(() -> {            bankAccount1.transfer(100, bankAccount2);        });        Thread thread2 = new Thread(() -> {            bankAccount2.transfer(50, bankAccount1);        });        thread1.start();        thread2.start();        thread1.join();        thread2.join();    }    @ConcurrencyTest(iterations = 10)    public void runMyBankAccountTestUsingFray() throws InterruptedException {        myBankAccountTest();    }}
复制代码

 

运行上述测试,4 次(共 10 次)迭代后出现以下错误:

 

[ERROR] Errors: [ERROR] org.example.BankAccountTest.runMyBankAccountTestUsingFray [INFO] Run 1: PASS [INFO] Run 2: PASS [INFO] Run 3: PASS [ERROR] Run 4: BankAccountTest.runMyBankAccountTestUsingFray:33->myBankAccountTest:25->Object.wait:-1 » Deadlock [INFO] Run 5: PASS [INFO] Run 6: PASS [INFO] Run 7: PASS [INFO] Run 8: PASS [INFO] Run 9: PASS [INFO] Run 10: PASS

 

或者,对于 JUnit 之外的其他测试框架,可以使用 FrayInTestLauncher 类:

 

public void myTest() {    FrayInTestLauncher.INSTANCE.launchFrayTest(() -> { … });}
复制代码

 

Fray 会在测试失败时自动生成一个测试用例用于重现失败过程。详细信息记录在报告文件夹中。使用 Maven 时,报告文件夹位于 target/fray/fray-report 目录内。使用这个文件夹可以通过两种不同的方式重现失败过程。

 

第一种方案是使用相同的调度器和记录的随机选择重新运行测试,具体方法详见论文:分布式系统设计的反馈引导自适应测试。因此,应在 ConcurrencyTest 注解中设置重放文件的路径:

 

@ConcurrencyTest(        replay = "[path to report]/recording")
复制代码

 

立即重新运行测试,出现以下错误:

 

Error: org.pastalab.fray.runtime.DeadlockException
复制代码

 

在示例应用程序内部解决死锁后,测试通过。

 

第二种方法是使用从原执行中观察到的精确线程调度再次执行测试。因此,需要使用以下 Java 选项记录调度信息:-Dfray.recordSchedule=true。记录调度信息后,应在 ConcurrencyTest 注解中使用 ReplayScheduler 类:

 

@ConcurrencyTest(    scheduler = ReplayScheduler.class,    replay = "[path to report]/recording")
复制代码

 

其他可用于检测 Java 代码并发问题的可选框架包括VMLensJava Concurrency Stress(jcstress)以及 IntelliJ IDEA 的Lincheck。要了解有关 Fray 的更多信息,请参阅使用指南技术报告

 

原文链接:

https://www.infoq.com/news/2025/12/fray-detects-concurrency-issues/

2025-12-11 16:501

评论

发布
暂无评论

柯桥插花花艺培训到兴德!良心机构!

Geek_196d9f

初步认识 Stripe 支付

DoneSpeak

Payment

Protobuf与POJO的相互转化 - 通过Json

DoneSpeak

json protobuf serialization

Spring Event初步讲解

DoneSpeak

spring

Spring Security认证流程

DoneSpeak

spring security springsecurity

1.3面向复杂度的架构设计

Lemon

架构设计 架构设计原则

只有思考清晰,才能表达有力!

云祁

读书 7月日更

SpringMVC | Controller 返回值及异常的统一处理

DoneSpeak

spring RESTful

漏洞挖掘分析技术总结

网络安全学海

运维 网络安全 信息安全 渗透测试· 漏洞分析

架构设计方法论

king

为easyexcel设置TimeZone

DoneSpeak

Excel Apache POI

如何画好架构图

king

用回溯法计算消消乐游戏最大得分

DoneSpeak

algorithm

Java 工具箱 | 图片-Base64 互转

DoneSpeak

Protobuf与Json的相互转化

DoneSpeak

json protobuf serialization

如何做好架构设计?

king

SpringBoot解决CORS问题

DoneSpeak

springboot

架构训练营模块七作业

Geek_e0c25c

架构实战营

我用一个例子疏通“路由器漏洞&复现”【建议收藏!!】

网络安全学海

运维 网络安全 信息安全 漏洞分析 代码复现

从Ftrace开始内核探索之旅

mazhen

Linux debug Trace Linux Kenel

MySql 全文检索两个字符的内容无法得到结果

DoneSpeak

MySQL

LeetCode | 13. 罗马数字转整数

DoneSpeak

LeetCode algorithm

柯桥会计培训到兴德教育!良心机构!

Geek_196d9f

【得物技术】浅谈资损防控

得物技术

测试 质量 稳定性 稳定性测试 资产管理

Git-Flow规范和指令

DoneSpeak

git Teamwork

实现自己的Protobuf Any

DoneSpeak

protobuf

柯桥摄影培训到兴德教育!良心机构!

Geek_196d9f

n 阶幻方问题

DoneSpeak

algorithm

CabloyJS 基于 EggJS 实现的模块编译与发布

node.js 全栈

模块7作业

wade

#架构实战营

推荐系统的UI交互与视觉展示(二十七)

Databri_AI

人工智能 算法 推荐系统

使用Fray检测JVM语言中的并发问题_编程语言_Johan Janssen_InfoQ精选文章