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支持多线程,有兴趣的读者自行度娘...

上一篇:Linux C 互斥锁的使用


下一篇:线上zk节点报org.apache.zookeeper.server.NIOServerCnxnFactory.run(NIOServerCnxnFactory.java:187) at java.lang.Thread.run(libgcj.so.10)