Threads Collider:一个Java多线程测试工具


Threads Collider尝试在“完全相同”的时刻对多个线程执行所需的操作,以增加出现由竞争条件或死锁引起的问题的机会。


code@RepeatedTest(10)
void Thread_safe_adding_to_list() {
    // Given
    List<String> list = new ArrayList<>();    // <-- NOT thread safe

    // When
    try (ThreadsCollider threadsCollider =
             threadsCollider()
                 .withAction(() -> list.add("bar"))
                 .times(Processors.ALL)   // run action on all available processors
                 .build()) {

        threadsCollider.collide();
    }

    // Then
    then(list).hasSize(Processors.ALL).containsOnly("bar");
}/code

死锁测试:
codevoid update1(List<Integer> list1, List<Integer> list2) {

    synchronized (list1) {
        list1.add(1);
        synchronized (list2) {
            list2.add(1);
        }
    }
}
void update2(List<Integer> list2, List<Integer> list1) {

    synchronized (list2) {
        list2.add(1);
        synchronized (list1) {
            list1.add(1);
        }
    }
}/code

希望同时执行这两个方法以显示死锁:


code@RepeatedTest(10)
void Detect_deadlock() {
    // Given
    List<Exception> exceptions = new ArrayList<>();

    // When
    try (ThreadsCollider collider =
             threadsCollider()
                 .withAction(() -> update1(list1, list2), "update1") // add action name for better logs readability
                 .times(Processors.HALF)
                 .withAction(() -> update2(list2, list1), "update2") // add action name for better logs readability
                 .times(Processors.HALF)
                 .withThreadsExceptionsConsumer(exceptions::add)     // save threads exceptions
                 .withAwaitTerminationTimeout(100)
                 .asMilliseconds()
                 .build()) {

        collider.collide();
    }

    // Then
    then(exceptions).isEmpty();
}/code
某些测试将失败并显示以下消息:
codejava.lang.AssertionError: 
Expecting empty but was: pl.amazingcode.threadscollider.UnfinishedThreads: 
There are threads that have not completed within the specified timeout: 100 MILLISECONDS
Check if there are any deadlocks and fix them. 
If there are no deadlocks, increase timeout.
Deadlocked threads:
"collider-pool-thread-2 update1" daemon prio=5 Id=24 BLOCKED on java.util.ArrayList@6c6cb480 owned by "collider-pool-thread-3 update2" Id=25

"collider-pool-thread-3 update2" daemon prio=5 Id=25 BLOCKED on java.util.ArrayList@3eb738bb owned by "collider-pool-thread-2 update1" Id=24

/code

依赖:
code<dependency>
    <groupId>pl.amazingcode</groupId>
    <artifactId>threads-collider</artifactId>
    <version>1.0.3</version>
    <scope>test</scope>
</dependency>/code