在Java中测试并发

LiT*_*Tle 5 java testing concurrency multithreading blocking

根据清单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();
 }
}
Run Code Online (Sandbox Code Playgroud)

假设执行了以下步骤:

  1. taker 线程启动。
  2. bb.take()成功返回,我们只需要一点点fail()方法就可以运行。
  3. 这称为interrupt()方法。
  4. 我们处于线程的catch障碍中taker

因此,我们目前处于困境,但实际上测试方法失败了。它失败了,我们从不知道。

这是正确的吗?如果是,我们该如何解决?

ass*_*ias 1

take应该阻塞在空队列上。所以预期的事件顺序是:

\n\n
    \n
  • taker.start();=> 启动线程
  • \n
  • Thread.sleep(LOCKUP_DETECT_TIMEOUT);等待以确保线程已启动并take已被调用。常量的实际值很难估计,但是任何高于几百毫秒的值都应该足够了 - 或者,您可以使用 CountDownLatch 来了解接受者线程何时启动
  • \n
  • 在接受者线程中:bb.take();=> 应该被阻止 - 如果不fail()被调用并且测试失败
  • \n
  • 在主线程中:taker.interrupt();=> 该take()方法应该退出InterruptedException
  • \n
  • 在主线程中:taker.join();=> 等待一段时间以允许接受者线程完成
  • \n
  • 在主线程中:assertFalse(taker.isAlive());=> 确认接受者线程已经退出并且不再被阻塞在take方法中
  • \n
\n\n

带闩锁的版本(假设如果线程在调用之前 被中断,则会以 InterruptedException 退出 - 如果不是,那么你没有其他方法,只能在调用之前添加一些随机睡眠):taketakestarted.await()

\n\n
void testTakeBlocksWhenEmpty() {\n final CountDownLatch started = new CountDownLatch(1);\n final CountDownLatch ended = new CountDownLatch(1);\n final BoundedBuffer<Integer> bb = new BoundedBuffer<Integer>(10);\n Thread taker = new Thread() {\n  public void run() {\n   try {\n    started.countDown();\n    int unused = bb.take();\n    fail(); // if we get here, it\xe2\x80\x99s an error\n   } catch (InterruptedException success) { }\n   ended.countDown();\n  }\n };\n try {\n  taker.start();\n  started.await();\n  taker.interrupt();\n  assertTrue(ended.await());\n } catch (Exception unexpected) {\n  fail();\n }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

您应该向测试方法或锁存器添加超时(足够长,以便在测试通过时不会干扰,例如 5 秒)。这将避免阻塞您的整个测试套件。

\n