Spec-Zone.ru › Kotlin 2

Тестирование произвольного кода

Lincheck предоставляет функцию runConcurrentTest() для тестирования произвольного конкурентного кода.

Функция runConcurrentTest() выполняет блок конкурентного кода несколько раз и использует проверку модели для исследования возможных расписаний выполнения.

Чтобы протестировать конкурентный код с помощью Lincheck:

  1. Создайте тестовый класс:

    class CounterTestWithInvocations {
    }
  2. Создайте тестовую функцию как функцию-член, используя runConcurrentTest():

        @Test
        fun test() = Lincheck.runConcurrentTest(100_000) {
            var counter = 0
    
            // Increments the counter concurrently
            val t1 = thread { counter++ }
            val t2 = thread { counter++ }
    
            // Waits for the threads to finish
            t1.join()
            t2.join()
    
            // Checks that both increments have been applied
            assertEquals(2, counter)
        }
    

    Параметр функции необязателен; он задаёт количество расписаний выполнения для исследования. Значение по умолчанию — 10_000.

  3. Запустите тест. Если он завершится неудачно, 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  |               |               |
    | ------------------------------------------------------------------------------- |
    

Пример: тестирование функций ConcurrentHashMap

Рассмотрим этот тест для функций ConcurrentHashMap:

package org.lincheck.docs

import org.jetbrains.lincheck.*
import kotlin.test.Test
import java.util.concurrent.*
import kotlin.concurrent.*

// This test demonstrates a deadlock caused by two threads
// performing nested `computeIfAbsent` calls in opposite order.
class ConcurrentHashMapDeadlockTest {
    @Test
    fun test() = Lincheck.runConcurrentTest {
        val map = ConcurrentHashMap<String, String>()

        // Updates `key2` while locking `key1`.
        val thread1 = thread {
            map.computeIfAbsent("key1") {
                map.computeIfAbsent("key2") { "value2" }
                "value1"
            }
        }
        
        // Updates `key1` while locking `key2`.
        val thread2 = thread {
            map.computeIfAbsent("key2") {
                map.computeIfAbsent("key1") { "value1" }
                "value2"
            }
        }

        // Wait until both threads complete.
        thread1.join()
        thread2.join()
    }
}

Тест завершается неудачно, поскольку Lincheck находит расписание выполнения, приводящее к взаимной блокировке:

  1. Поток 2 сопоставляет key2 корзине с индексом 1, блокирует эту корзину и начинает выполнять computeIfAbsent("key1"). Выполнение переключается с потока 2 на поток 1 до того, как поток 2 сопоставит key1 и заблокирует корзину с key1.

  2. Поток 1 сопоставляет key1 корзине с индексом 0, блокирует эту корзину и начинает выполнять computeIfAbsent("key2"). Поток 1 сопоставляет key2 корзине с индексом 1 и пытается заблокировать эту корзину, но она уже заблокирована потоком 2. Выполнение переключается с потока 1 на поток 2.

  3. Поток 2 пытается заблокировать корзину с key1, но она уже заблокирована потоком 1.

Оба потока заблокированы, поэтому выполнение привело к взаимной блокировке.

Что дальше

Узнайте, как тестировать структуры данных с помощью Lincheck.

См. также

  • Проверка модели в Lincheck

  • Lincheck в проектах Kotlin Multiplatform

15 августа 2026 г.
Начало работы с LincheckКак тестировать структуры данных

© 2010–2026 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/lincheck-testing-arbitrary-code.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API