Java高并发测试框架JCStress

前言

若是要研究高并发,通常会借助高并发工具来进行测试。JCStress(Java Concurrency Stress)它是OpenJDK中的一个高并发测试工具,它能够帮助咱们研究在高并发场景下JVM,类库以及硬件等情况。html

JCStress学起来很简单,并且官方也提供了许多高并发场景下的测试用例,只要引入一个jar包,便可运行研究。java

如何使用JCStress

此演示用maven工程,首先须要引入jar包,核心包是必需要的,样例包非必需要,此是为了演示其中的例子。git

<dependencies>
    <!-- jcstress 核心包 -->
    <dependency>
        <groupId>org.openjdk.jcstress</groupId>
        <artifactId>jcstress-core</artifactId>
        <version>0.3</version>
    </dependency>
    <!-- jcstress测试用例包 -->
    <dependency>
        <groupId>org.openjdk.jcstress</groupId>
        <artifactId>jcstress-samples</artifactId>
        <version>0.3</version>
    </dependency>
</dependencies>

先写一个简单测试用例,一些注解不明白能够先无论,后面会讲解。此样例会在高并发下调用actor1和actor2方法各一次,按照正常逻辑,x最后的值要么是-1要么是5,若是actor2方法内的2行代码发生了指令重排序,就会致使x的值可能为0。github

package com.nobody;

import org.openjdk.jcstress.annotations.*;
import org.openjdk.jcstress.infra.results.I_Result;

/**
 * @Description 测试指令重排序
 * @Author Mr.nobody
 * @Date 2021/4/6
 * @Version 1.0
 */
@JCStressTest // 标记此类为一个并发测试类
@Outcome(id = {"0"}, expect = Expect.ACCEPTABLE_INTERESTING, desc = "wrong result") // 描述测试结果
@Outcome(id = {"-1", "5"}, expect = Expect.ACCEPTABLE, desc = "normal result") // 描述测试结果
@State //标记此类是有状态的
public class TestInstructionReorder {

    private boolean flag ;
    private int x;

    public TestInstructionReorder() {}

    @Actor
    public void actor1(I_Result r) {
        if (flag) {
            r.r1 = x;
        } else {
            r.r1 = -1;
        }
    }

    @Actor
    public void actor2(I_Result r) {
        this.x = 5;
        flag = true;
    }
}

配置程序的主类,org.openjdk.jcstress.Main是JCStress自带的一个启动类;而后能够配置-t参数设置须要测试的类,固然 -t 后面也能够指定包名,表示执行指定包下的全部测试类。若是不指定-t参数,默认会扫描项目下全部包的类。正则表达式

运行程序,结果显示,x的值出现了0,-1,5三种结果,其中值为0不是我期待的,可是它在高并发下确实出现了,虽然相比其余值(几十万次)出现的几率(200屡次)很低。数组

有些人会说用jmeter工具不也能够测试高并发,可是它们的侧重点仍是不同的,jmeter侧重对于接口总体的响应速度等进行测试,而JCStress框架能对某块逻辑代码进行高并发测试,更加侧重JVM,类库等领域的研究。bash

并且,JCStress会考虑不一样JVM参数设置下的测试,并且自动帮咱们设置,例如上图所示[-XX:-TieredCompilation]。数据结构

除了命令行窗口显示的测试结果以外,还会在项目所在的目录下生成 results文件夹,生成测试结果文档,其中index.html是测试总览,其余html文件是每一个测试类的报告,结合结果数据结构可视化图形更加容易理解。并发

JCStress 注解说明

@JCStressTestapp

标记一个类为并发测试的类,它有一个org.openjdk.jcstress.annotations.Mode枚举类型的属性value。Mode.Continuous模式表示会运行几个Actor,Ariter线程,并收集统计结果。Mode.Termination模式表明运行具备阻塞/循环操做的单个Actor,看是否响应Singal信号。

@State

标记一个类是有状态的,即拥有能够读写的数据,例如上例的x和falg。State类只能是public的,不能是内部类(能够是静态内部类),而且得有一个默认构造方法。

@Outcome

描述测试的结果,它有3个属性,id属性为一个字符串数组,表示接收的结果,支持正则表达式;expect表示对观测结果的指望,它的值是一个枚举值;desc属性指定一个易于人类理解的对结果的描述。@Outcomes注解能够组合多个结果注解。

@Actor

@Actor是一个中心测试注解,它标记的方法会被一个特定的线程调用,每个对象的方法只能被调用一次。多个Actro方法调用顺序是不保证的,它们是并发执行的,方法能够抛出异常而且会致使测试失败。Actor方法所在的类必须有State或者Result注解。

@Arbiter

它的做用其实和@Actor差很少,可是Arbiter标记的方法调用是在全部@Actor标记的方法调用以后,因此它标记的方法通常做为收集最后的结果来使用。

@Signal

此注解也是标记方法的,可是它是在JCStressTest的Termination模式下工做的,它的调用是在全部Actor以后。

@Result

它标记的类被做为测试结果的类,JCStress自带的org.openjdk.jcstress.infra.results包下就有大量的测试结果类,不一样的类能够用来保持不一样的结果。例如I_Result类有一个int类型的变量r1;II_Result类有2个int类型的变量r1和r2。

JCStress 插件

有一个插件集成了JCStress和Gradle,咱们只须要在build.gradle中引入此插件,便可使用插件命令来进行测试。插件依赖为jcstress-gradle-plugin。

build.gradle文件以下,不一样版本的插件集成了默认的JCStress版本,固然咱们也能够自定义更改,以下最后一行所示。

apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'jcstress'

buildscript {
    repositories {
        jcenter()
    }

    dependencies {
        classpath 'com.github.erizo.gradle:jcstress-gradle-plugin:0.8.1'
    }
}


ext {
    jcstressVersion = '0.7'
}

repositories {
    jcenter()
}

dependencies {
    compile "org.openjdk.jcstress:jcstress-core:${jcstressVersion}"
}

jcstress {
    jcstressDependency "org.openjdk.jcstress:jcstress-core:${jcstressVersion}"
}

而后在项目便可编写测试类,例如仍是上面那个例子,最后咱们在项目根目录下执行 gradle jcstress,便可显示测试结果。也能够经过参数指定要测试的类,例如 gradle jcstress --tests "TestInstructionReorder"

插件源码地址:https://github.com/jerzykrlk/jcstress-gradle-plugin

相关文章
相关标签/搜索