zoukankan      html  css  js  c++  java
  • 并发测试工具:jcstress

    简介

    jcstress:全名The Java Concurrency Stress tests,是一个实验工具和一套测试工具,用于帮助研究JVM、类库和硬件中并发支持的正确性。

    官方github:https://github.com/openjdk/jcstress

    官方给出的jcstress使用案例:http://hg.openjdk.java.net/code-tools/jcstress/file/tip/jcstress-samples/src/main/java/org/openjdk/jcstress/samples

    依赖

            <dependency>
                <groupId>org.openjdk.jcstress</groupId>
                <artifactId>jcstress-core</artifactId>
                <version>0.5</version>
            </dependency>
            <dependency>
                <groupId>org.openjdk.jcstress</groupId>
                <artifactId>jcstress-samples</artifactId>
                <version>0.5</version>
            </dependency>
    

    HelloWorld

    @JCStressTest
    @Outcome(id = "1, 1", expect = Expect.ACCEPTABLE_INTERESTING, desc = "Both actors came up with the same value: atomicity failure.")
    @Outcome(id = "1, 2", expect = Expect.ACCEPTABLE, desc = "actor1 incremented, then actor2.")
    @Outcome(id = "2, 1", expect = Expect.ACCEPTABLE, desc = "actor2 incremented, then actor1.")
    @State
    public class APISample_01_Simple {
    
        int v;
    
        @Actor
        public void actor1(II_Result r) {
            r.r1 = ++v; // record result from actor1 to field r1
        }
    
        @Actor
        public void actor2(II_Result r) {
            r.r2 = ++v; // record result from actor2 to field r2
        }
    
    }
    

    是不是一脸懵逼?一堆不认识的注解,且听我细细道来。

    @JCStressTest:

    该注解标记一个类为一个并发测试的类,有一个属性 value 为Mode。mode 有ContinuousTermination 两种模式。Continuous 模式是运行几个 Actor 、Ariter 线程,并收集结果。Termination 模式运行具有阻塞/循环操作的单个 Actor 和 Singal 的测试。

    @Outcome:

    描述测试结果,并处理这个结果,该注解有 idexpectdesc 这三个属性。其中 id 接收结果,id 还支持正则表达式;expect 是期望处理结果,有 ACCEPTABLEACCEPTABLE_INTERESTINGFORBIDDENUNKNOWN 四种类型,ACCEPTABLE 表示接受当前结果,ACCEPTABLE 结果不一定会存在;ACCEPTABLE_INTERESTING 和 ACCEPTABLE 差不多,唯一不一样的是,这个结果会在生成的报告中高亮;FORBIDDEN 表示永远不应该出现的结果,若测试过程中有该结果,意味着测试失败; UNKNOWN 没有评分,不使用。

    @State:

    标记这个类是有状态的,有状态的意识是拥有数据,而且数据是可以被修改的,如上面测试例子中的 v , 其中 v 就是拥有的数据。State 修饰的类必须是 public 的,不能是内部类,但是可以是静态内部类,如上面例子。State 修饰的类必须有一个默认构造函数

    @Actor:

    该注解标记的方法会被线程调用,被 Actor 修饰方法所在的类必须有 State 或者 Result注解,被其修饰的方法可以抛出异常,但是抛出异常的话,会引起测试失败。注意的是,Actor 标记的每个方法仅由一个特定线程调用,而且每个被 State 标记的实例仅调用每一个方法Actor 修饰的方法之间是没有顺序的,调用是并发执行的。

    那么如何运行呢?并没有发现有运行的入口。

    配置idea:

    添加一个application

    image-20210121212949920

    这里配置需要注意:main class必须写成org.openjdk.jcstress.Main,此外需要配置包名 -t package path,package path具体和你的测试类所在的包一样。

    image-20210121213428818

    配置完成后,运行,等待一小段时间。

    image-20210121213318019

    运行成功后,在我们项目根路径下生成一个results文件夹,打开里面的index.html可以查看测试结果

    image-20210121213606191

    image-20210121214039399

    @Result

    在上一个案例中,我们使用II_Result类去收集两个线程返回的结果(int类型),并在Outcome注解的id属性中列举了所有可能出现的情况。那我们如果想要收益一个int的返回值,或者收集float、char、byte等其他基本类型和Object类型,应该用什么类?

    实际上jcstress已经在org.openjdk.jcstress.infra.results包下列举了很多返回值类型供我们在平时开发测试中使用。

    image-20210121222106002

    我们可以使用@Result注解自定义返回值类型,如下图,该注解标记的类所有 field 都必须是原生数据类似或者是 String 类型,所有 field 都应该是 publi。不过需要自定义的情况几乎没有

    image-20210121222457487

    @Arbiter

    Arbiter 注解和 Actor 注解差不多,不一样的是 Arbiter 注解声明的方法运行在所有 Actor之后,而且 Actor 所有的内存都对 Arbiter 可见,这就使得 Arbiter 在确认最终状态信息上有很大的作用

    @JCStressTest
    // These are the test outcomes.
    @Outcome(id = "1", expect = Expect.ACCEPTABLE_INTERESTING, desc = "One update lost: atomicity failure.")
    @Outcome(id = "2", expect = Expect.ACCEPTABLE, desc = "Actors updated independently.")
    @State
    public class APISample_02_Arbiters {
    
        int v;
    
        @Actor
        public void actor1() {
            v++;
        }
    
        @Actor
        public void actor2() {
            v++;
        }
    
        @Arbiter
        public void arbiter(I_Result r) {
            r.r1 = v;
        }
    
    }
    

    image-20210121214611754

    @Signal

    一些并发测试没有遵循Mode.Continuous。一个有趣的测试组之一是断言代码是否在一个信号。

    在这里,我们使用一个@Actor,等待一个字段,以及一个@Signal设置该字段。JCStress会启动actor,然后传递信号。

    如果在合理时间内退出,则记录“TERMINATED”结果,否则记录“STALE”。

    public class APISample_03_Termination {
        @JCStressTest(Mode.Termination)
        @Outcome(id = "TERMINATED", expect = Expect.ACCEPTABLE, desc = "Gracefully finished.")
        @Outcome(id = "STALE", expect = Expect.ACCEPTABLE_INTERESTING, desc = "Test hung up.")
        @State
        public static class NotHasVolatile{
            int v;
            @Actor
            public void actor1() {
                while (v == 0) {
                    // spin
                }
            }
            @Signal
            public void signal() {
                v = 1;
            }
        }
        @JCStressTest(Mode.Termination)
        @Outcome(id = "TERMINATED", expect = Expect.ACCEPTABLE, desc = "Gracefully finished.")
        @Outcome(id = "STALE", expect = Expect.ACCEPTABLE_INTERESTING, desc = "Test hung up.")
        @State
        public static class HasVolatile{
            volatile int v;
            @Actor
            public void actor1() {
                while (v == 0) {
                    // spin
                }
            }
            @Signal
            public void signal() {
                v = 1;
            }
        }
    }
    

    有volatile关键字:

    image-20210121215834648

    无volatile关键字:

    image-20210121215858034

    测试结果符合预期。

    @ JCStressMeta元数据共享

    Description 、Outcome、Ref 这些注解是可以放到一个公共类,然后由@ JCStressMeta 注解引进来,以达到重复使用的目的。

    @Outcome(id = "TERMINATED", expect = Expect.ACCEPTABLE, desc = "Gracefully finished.")
    @Outcome(id = "STALE", expect = Expect.ACCEPTABLE_INTERESTING, desc = "Test hung up.")
    public class APISample_05_SharedMetadata {
        @JCStressTest(Mode.Termination)
        @JCStressMeta(APISample_05_SharedMetadata.class)
        @State
        public static class NotHasVolatile{
            int v;
            @Actor
            public void actor1() {
                while (v == 0) {
                    // spin
                }
            }
            @Signal
            public void signal() {
                v = 1;
            }
        }
        @JCStressTest(Mode.Termination)
        @JCStressMeta(APISample_05_SharedMetadata.class)
        @State
        public static class HasVolatile{
            volatile int v;
            @Actor
            public void actor1() {
                while (v == 0) {
                    // spin
                }
            }
            @Signal
            public void signal() {
                v = 1;
            }
        }
    }
    

    Descriptions描述信息

    使用@Description和@Ref可以描述测试信息。只需要在测试类的上标注即可,对实际测试结果并无影响。

    image-20210121220611849

    实战:AtomicInteger原子性测试

    @JCStressTest
    @Outcome(id = "1", expect = Expect.FORBIDDEN,  desc = "One update lost.")
    @Outcome(id = "2", expect = Expect.ACCEPTABLE, desc = "Both updates.")
    @State
    public class AtomicIncrementTest {
        AtomicInteger ai = new AtomicInteger();
    
        @Actor
        public void actor1() {
            ai.incrementAndGet();
        }
    
        @Actor
        public void actor2() {
            ai.incrementAndGet();
        }
    
        @Arbiter
        public void arbiter(I_Result r) {
            r.r1 = ai.get();
        }
    }
    

    并发测试结果:

    image-20210121221014747

    由图中结果Both updates,可知AtomicInteger的incrementAndGet具有原子性。

    部分参考:https://www.cnblogs.com/jfound/p/12975617.html

  • 相关阅读:
    kafka系列教程5(客户端实践)
    log4j示例-Daily方式(log4j.properties)
    Log4j.xml配置(rolling示例)
    Log4j配置详解之log4j.xml
    kafka 常用命令
    linux下进程cpu占用过高问题定位方法
    Linux 操作系统主机名变成bogon怎么解决?
    网络基础 Windows控制台下Ftp使用简介
    网络基础 cookie详解
    网络基础 http 会话(session)详解
  • 原文地址:https://www.cnblogs.com/wwjj4811/p/14310930.html
Copyright © 2011-2022 走看看