1. 程式人生 > 其它 >Junit單元測試無法正常啟用多執行緒

Junit單元測試無法正常啟用多執行緒

測試Redis setnx實現分散式鎖,使用Junit單元測試,但是每次執行5、6秒程式就退了,然後報了redis相關的一堆錯,一直以為是redis的原因,各種查,然後發現,淦,就是因為Junit單元測試不支援多執行緒

原博文:Junit單元測試多執行緒的問題

部分Junit4 TestRunner原始碼:

public static final int SUCCESS_EXIT = 0;
public static final int FAILURE_EXIT = 1;
public static final int EXCEPTION_EXIT = 2;

public static void main(String args[]) {
    TestRunner aTestRunner = new TestRunner();
    try {
        TestResult r = aTestRunner.start(args);
        if (!r.wasSuccessful())
            System.exit(FAILURE_EXIT);
        System.exit(SUCCESS_EXIT);
    } catch (Exception e) {
        System.err.println(e.getMessage());
        System.exit(EXCEPTION_EXIT);
    }
}

TestResult部分原始碼:

protected  List<TestFailure>    fFailures
protected  List<TestFailure>    fErrors

public synchronized boolean wasSuccessful() {
    return failureCount() == 0 && errorCount() == 0;
}

public synchronized int errorCount() {
    return fErrors.size();
}

public synchronized int failureCount() {
    return fFailures.size();
}

在TestRunner中,如果是單執行緒,當測試主執行緒執行結束後,不管子執行緒是否結束,都會回撥TestResult的wasSuccessful方法,
然後判斷結果是成功還是失敗,最後呼叫相應的System.exit()方法。這個方法是用來結束當前正在執行中的java虛擬機器,jvm都自身難保了,所以子執行緒也就結束了。

解決辦法:

1 簡單粗暴地讓主執行緒休眠一段時間,然後讓子執行緒能夠執行結束。但是這個方法的弊端是,你不知道子執行緒的執行時間,所以需要看臉

Thread.sleep();

2 使用CountDownLatch工具類,讓主執行緒阻塞,直到子執行緒執行結束或者阻塞超時,這個方法要比第一個方法好點。

countDownLatch.await(5, TimeUnit.MINUTES);

測試程式碼

	@Autowired
    private RedisService redisService;

    @Test
    void test1() {
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    Boolean setnx = redisService.setnx("TASK_CHECK_SCHEME_STATE_LOCK", System.currentTimeMillis(), 1, TimeUnit.SECONDS);
                    System.out.println("===========thread1 lock =======" + setnx);
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    Boolean setnx = redisService.setnx("TASK_CHECK_SCHEME_STATE_LOCK", System.currentTimeMillis(), 1, TimeUnit.SECONDS);
                    System.out.println("==============thread2 lock ============ "+setnx);
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        thread1.start();
        thread2.start();

        // 暫停主執行緒
        CountDownLatch countDownLatch = new CountDownLatch(1);
        try {
            countDownLatch.await(10, TimeUnit.HOURS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

使用countDownLatch.await(10, TimeUnit.HOURS);使主執行緒不立刻結束,即可實現在Junit測試單元實現多執行緒。

===========thread1 lock =======true
==============thread2 lock ============ false
===========thread1 lock =======false
==============thread2 lock ============ false
===========thread1 lock =======false
==============thread2 lock ============ false
===========thread1 lock =======false
==============thread2 lock ============ false
===========thread1 lock =======false
==============thread2 lock ============ false
===========thread1 lock =======false
==============thread2 lock ============ false
...

至於還有其他方法,筆者看到很多大神自己寫的Junit支援多執行緒,有興趣的讀者自行度娘...