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