根据清单12.3中的Java Concurrency in Practice书,我们可以使用以下示例代码测试并发代码:
void testTakeBlocksWhenEmpty() {
final BoundedBuffer<Integer> bb = new BoundedBuffer<Integer>(10);
Thread taker = new Thread() {
public void run() {
try {
int unused = bb.take();
fail(); // if we get here, it’s an error
} catch (InterruptedException success) { }
}
};
try {
taker.start();
Thread.sleep(LOCKUP_DETECT_TIMEOUT);
taker.interrupt();
taker.join(LOCKUP_DETECT_TIMEOUT);
assertFalse(taker.isAlive());
} catch (Exception unexpected) {
fail();
}
}
假设执行了以下步骤:
taker
线程已启动。 bb.take()
成功返回,距离fail()
方法运行还有点距离。 interrupt()
方法。 catch
线程的taker
块。 因此,我们目前处于困境,但实际上测试方法失败了。它失败了,我们从不知道。
这是正确的吗?如果是,我们如何解决?
最佳答案
take
应该在空队列中阻塞。因此,预期的事件顺序为:
taker.start();
=>启动线程Thread.sleep(LOCKUP_DETECT_TIMEOUT);
等待确定线程已启动并已调用take
。常量的实际值很难估计,但是任何超过几百毫秒的值都应该足够-或者,您可以使用CountDownLatch来了解taker线程何时启动bb.take();
=>应该阻止-如果未调用fail()
且测试失败taker.interrupt();
=>应当使用take()
退出InterruptedException
方法taker.join();
=>等待一段时间,以使接受者线程完成assertFalse(taker.isAlive());
=>确认接收者线程已退出并且在take
方法中不再受阻带有闩锁的版本(它假定如果线程在调用
take
之前被中断,则take
将以InterruptedException退出-如果没有,则您别无选择,只能在调用started.await()
之前添加一些随机睡眠):void testTakeBlocksWhenEmpty() {
final CountDownLatch started = new CountDownLatch(1);
final CountDownLatch ended = new CountDownLatch(1);
final BoundedBuffer<Integer> bb = new BoundedBuffer<Integer>(10);
Thread taker = new Thread() {
public void run() {
try {
started.countDown();
int unused = bb.take();
fail(); // if we get here, it’s an error
} catch (InterruptedException success) { }
ended.countDown();
}
};
try {
taker.start();
started.await();
taker.interrupt();
assertTrue(ended.await());
} catch (Exception unexpected) {
fail();
}
}
您应该在测试方法或锁存器中添加一个超时(足够长,以免在测试通过时发生干扰,例如5秒)。这样可以避免阻塞整个测试套件。