Kotlin 语言参考文档 中文版 Help

Lincheck 入门

这篇快速入门向导将引导你, 完成 Lincheck 的设置, 编写你的第一个 Lincheck 测试, 并解读测试报告.

你将会:

  • 创建一个新的 IntelliJ IDEA 项目, 并安装 Lincheck.

  • 编写你的第一个并发测试, 并使用 Lincheck 运行它.

  • 创建一个并发数据结构, 并使用两种测试策略, 通过 Lincheck 进行测试.~~

创建项目

在 IntelliJ IDEA 中打开一个既有的 Kotlin 项目, 或者 创建一个新项目.

添加依赖项

要在项目中使用 Lincheck, 请在你的构建配置中添加相应的依赖项:

// build.gradle.kts repositories { mavenCentral() } dependencies { testImplementation("org.jetbrains.lincheck:lincheck:3.6") testImplementation(kotlin("test")) }
// build.gradle repositories { mavenCentral() } dependencies { testImplementation "org.jetbrains.lincheck:lincheck:3.6" testImplementation "org.jetbrains.kotlin:kotlin-test" }
<!-- pom.xml --> <project> <dependencies> <dependency> <groupId>org.jetbrains.lincheck</groupId> <artifactId>lincheck</artifactId> <version>3.6</version> <scope>test</scope> </dependency> <dependency> <groupId>org.jetbrains.kotlin</groupId> <artifactId>kotlin-test</artifactId> <scope>test</scope> </dependency> </dependencies> ... </project>

编写你的第一个测试

对于一个基本的并发测试, 创建一个测试函数, 描述每个线程中应该执行的操作以及预期的断言. Lincheck 使用 模型检查(Model Checking) 探索程序可能的线程交叉执行, 并在出现错误行为时提供错误报告.

  1. src/test 目录中, 创建 CounterTest.kt 文件.

  2. 导入 org.jetbrains.lincheck, kotlinx.concurrent, 和 kotlin.test 库:

    import org.jetbrains.lincheck.* import kotlin.concurrent.* import kotlin.test.*
  3. 编写一个测试, 创建一个变量, 以及操作该变量的两个线程:

    class CounterTest { @Test // 声明测试函数 fun test() = Lincheck.runConcurrentTest { var counter = 0 // 并发的递增 counter val t1 = thread { counter++ } val t2 = thread { counter++ } // 等待线程结束 t1.join() t2.join() // 检查两次递增操作是否都已生效 assertEquals(2, counter) } }
  4. 运行测试. Lincheck 会生成一个报告, 其中包含导致错误行为的线程交叉执行:

    | ------------------------------------------------------------------------------- | | Main Thread | Thread 1 | Thread 2 | | ------------------------------------------------------------------------------- | | thread(block = Lambda#2): Thread#1 | | | | thread(block = Lambda#3): Thread#2 | | | | switch (reason: waiting for Thread 1 to finish) | | | | | | run() | | | | counter ➜ 0 | | | | switch | | | run() | | | | counter ➜ 0 | | | | counter = 1 | | | | | counter = 1 | | Thread#1.join() | | | | Thread#2.join() | | | | counter.element ➜ 1 | | | | assertEquals(2, 1): threw AssertionFailedError | | | | ------------------------------------------------------------------------------- |

    Lincheck 找到了一个线程交叉执行, 其中一个 inc() 操作覆盖了 counter 的值.

    逐步解读报告
    1. 在 Thread 2 中, JVM 读取了初始的 counter 值.

    2. 执行从 Thread 2 切换到 Thread 1.

    3. 在 Thread 1 中, JVM 递增了计数器. inc() 操作的所有步骤都不中断地执行完毕: 从变量读取值, 递增值, 并将值写回变量.

    4. 执行切换回 Thread 2.

    5. 在 Thread 2 中, JVM 将步骤 1 中获取的值加 1, 并将结果写入 counter 变量.

为数据结构编写测试

除了基本的并发测试之外, Lincheck 还支持以声明式方案测试并发数据结构.

要在 Lincheck 中测试数据结构, 你只需要声明数据结构的并发方法, 以及一个测试函数. Lincheck 会生成随机的并发场景, 使用指定的测试策略执行这些场景, 并提供错误报告.

本节中, 你将测试一个简单的计数器:

  1. src/test 目录中, 创建 CounterStructureTest.kt 文件.

  2. 导入 lincheck.datastructureskotlin.test 库:

    import org.jetbrains.lincheck.datastructures.* import kotlin.test.*
  3. 创建一个 Counter 数据结构:

    class Counter { @Volatile private var value = 0 fun inc(): Int = ++value fun get() = value }
  4. 创建 CounterStructureTest 类. 设置数据结构的初始状态, 并使用 @Operation 注解标记数据结构的并发操作:

    class CounterStructureTest { // 初始状态 private val c = Counter() // 并发操作 @Operation fun inc() = c.inc() @Operation fun get() = c.get() }
  5. CounterTest 类中, 使用 ModelCheckingOptions() 声明一个测试函数:

    @Test fun stressTest() = ModelCheckingOptions().check(this::class)

  6. 运行测试. Lincheck 会生成一个错误报告, 其中包含并发场景, 以及导致错误行为的具体线程交叉执行情况:

    | ------------------- | | Thread 1 | Thread 2 | | ------------------- | | inc(): 1 | inc(): 1 | | ------------------- |
    | ------------------------ | | Thread 1 | Thread 2 | | ------------------------ | | | inc(): 1 | | | c.inc(): 1 | | | value ➜ 0 | | | switch | | inc(): 1 | | | | value = 1 | | | value ➜ 1 | | | result: 1 | | ------------------------ |

下一步

阅读 测试策略 章节, 进一步了解数据结构测试的声明式方案, 以及支持的测试策略.

2026/07/24