JUC整理筆記三之測試工具jcstress


並發測試工具Jcstress使用教程

Jcstress 全稱 Java Concurrency Stress,是一種並發壓力測試工具,可以幫助研究JVM、java類庫和硬件中並發的正確性。

Wiki地址:https://wiki.openjdk.java.net/display/CodeTools/jcstress

Wiki中有一個Jcstress的example,可以根據example很方便入門使用jcstress。當然,我們也可以手動創建自己的Jcstress測試工程。

Maven例子

添加maven依賴

<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>

pom.xml 可以如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>jfound</groupId>
    <artifactId>jcstress-test</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <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>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>11</source>
                    <target>11</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

新增一個測試類,這次以測試變量加 volatile 和不加 volatile 的區別。代碼如下:

package jfound;
import org.openjdk.jcstress.annotations.*;

public class VolatileTest {
    @JCStressTest(Mode.Termination)
    @Outcome(id = "TERMINATED", expect = Expect.ACCEPTABLE)
    @Outcome(id = "STALE", expect = Expect.ACCEPTABLE_INTERESTING)
    @State
    public static class NoVolatile {
        private int i = 0;

        @Actor
        public void actor() {
            while (i == 0) {
                //nothing
            }
        }
        @Signal
        public void single() {
            i = 1;
        }
    }
    @JCStressTest(Mode.Termination)
    @Outcome(id = "TERMINATED", expect = Expect.ACCEPTABLE)
    @Outcome(id = "STALE", expect = Expect.FORBIDDEN)
    @State
    public static class AddVolatile {
        private volatile int i = 0;
        @Actor
        public void actor() {
            while (i == 0) {
                //nothing
            }
        }
        @Signal
        public void single() {
            i = 1;
        }
    }
}

VolatileTest 類里面有兩個靜態內部類,其中 NoVolatilei 是沒有加 volatile 關鍵字的,AddVolatile 類中的 i 是有加 volatile 關鍵字的。

測試注解說明

  • org.openjdk.jcstress.annotations.JCStressTest

該注解標記一個類為一個並發測試的類,有一個屬性 value 為 org.openjdk.jcstress.annotations.ModemodeContinuousTermination 兩種模式。Continuous 模式是運行幾個 org.openjdk.jcstress.annotations.Actororg.openjdk.jcstress.annotations.Ariter 線程,並收集結果。Termination 模式運行具有阻塞/循環操作的單個 ActorSingal 的測試。

  • org.openjdk.jcstress.annotations.Outcome

Outcome 是描述測試結果,並處理這個結果,該注解有 idexpectdesc 這三個屬性。

其中 id 接收結果,如上面的 TERMINATEDSTALE 就是結果,id 還支持正則表達式;expect 是期望處理結果,類型為 org.openjdk.jcstress.annotationsExpect ,有 ACCEPTABLEACCEPTABLE_INTERESTINGFORBIDDENUNKNOWN 四種類型,ACCEPTABLE 表示接受當前結果,ACCEPTABLE 結果不一定會存在;ACCEPTABLE_INTERESTING 和 ACCEPTABLE 差不多,唯一不一樣的是,這個結果會在生成的報告中高亮;FORBIDDEN 表示永遠不應該出現的結果,若測試過程中有該結果,意味着測試失敗; UNKNOWN 沒有評分,不使用。

如上面例子所示,一個測試類 (JCStressTest) 中可以有一個或多個 Outcome

  • org.openjdk.jcstress.annotations.State

標記這個類是有狀態的,有狀態的意識是擁有數據,而且數據是可以被修改的,如上面測試例子中的 i , 其中 i 就是擁有的數據。為了方便理解,可以拿 Stateless 來做對比;如一些實體類是有狀態(State)的,Service理應設置為無狀態的 Sateless ,盡管service有屬性,但是屬性也是不能被修改的。

State 修飾的類必須是 public 的,不能是內部類,但是可以是靜態內部類,如上面例子。

State 修飾的類必須有一個默認構造函數

  • org.openjdk.jcstress.annotations.Actor

該注解標記的方法會被線程調用,被 Actor 修飾方法所在的類必須有 State 或者 Result 注解,被其修飾的方法可以拋出異常,但是拋出異常的話,會引起測試失敗。注意的是,Actor 標記的每個方法僅由一個特定線程調用,而且每個被 State 標記的實例僅調用每一個方法Actor 修飾的方法之間是沒有順序的,調用是並發執行的。

與之相對的還有 org.openjdk.jcstress.annotations.Arbiter 注解,Arbiter 注解和 Actor 注解差不多,不一樣的是 Arbiter 注解聲明的方法訪問是在所有 State 之后,而且 Actor 所有的內存都對 Arbiter 可見,這就使得 Arbiter 在確認最終狀態信息上有很大的作用。

  • org.openjdk.jcstress.annotations.Singal

改注解在 Termination 模式下是比較有用的,它標記的方法執行是在 Actor 標記方法執行之后。

  • org.openjdk.jcstress.annotations.Result

用改注解標記的類是測試結果的類,如 org.openjdk.jcstress.infra.results 下的類,該注解標記的類所有 field 都必須是原生數據類似或者是 String 類型,所有 field 都應該是 public。可以參考 org.openjdk.jcstress.infra.results 下的類。用法如下

@Actor
public void actor1(I_Result r) {
    r.r1 = 1;
}

把測試結果用 Result 修飾的類接收,通常是配合 toString 打印出來。

  • org.openjdk.jcstress.annotations.Description

與測試無關,可以定義一些描述,方便查看

  • org.openjdk.jcstress.annotations.Ref

與測試無關,提供一些引用,如地址,ISBN等等。

  • org.openjdk.jcstress.annotations.JCStressMeta

DescriptionOutcomeRef 這些注解是可以放到一個公共類,然后由 JCStressMeta 注解引進來,以達到重復使用的目的。

運行及結果說明

在idea下執行如下

其中 Main class 填寫 org.org.openjdk.jcstress.Main , 參數中的 -t 是指指定測試包名,不指定的話是默認掃描項目所有包。

在執行完成后,會在項目所在的目錄下生成 results 文件夾,里面有個 index.html ,用瀏覽器打開即可查看報告。

總結

本文以一個例子展開來編寫 Jcstress 並發測試工具的使用說明,並描述了 Jcstress 的所有核心注解,方便初學者去了解並借助該工具來學習並發編程。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM