如何等待一个产生它自己的线程的线程?

我正在尝试测试一个在单独的线程中工作的方法,简化它是这样的:

public void methodToTest() { Thread thread = new Thread() { @Override public void run() { Clazz.i = 2; } }; thread.start(); } 

在我的unit testing中,我想测试Clazz.i == 2,但我不能这样做,因为我认为断言是在线程更改值之前运行的。 我想使用另一个线程来测试它,然后使用join等待,但它仍然无法正常工作。

SSCCE:

 @Test public void sscce() throws InterruptedException { Thread thread = new Thread() { @Override public void run() { methodToTest() } }; thread.start(); thread.join(); AssertEquals(2, Clazz.i); } public static class Clazz { public static int i = 0; } 

我认为这是因为测试主代码创​​建了一个等待(加入)第二个线程的线程,但是第二个线程没有完成工作,它创建另一个线程来完成工作然后完成,这继续第一个线程,而第三个线程在断言后执行Clazz.i = 2

我怎样才能使第一个线程等待它启动的线程以及该线程启动的任何线程

如果没有对methodToTest创建的线程的引用,那么就不能简单。 Java没有提供查找“在这个特定时间段内生成的线程”的机制(即使它确实如此,它也可能是一个丑陋的机制)。

在我看来,你有两个选择:

  • make methodToTest等待它产生的线程。 当然,如果您明确希望这是一个异步操作,那么您不能很好地做到这一点。
  • methodToTest返回新创建的线程,以便任何调用者可以选择等待它,如果他们愿意的话。

可以注意到,第二选择可以以几种不同的方式表达。 例如,如果你想扩展methodToTest的自由以使用各种方式进行异步工作,你可以返回一些抽象的类似Future的对象而不是一个线程。 您也可以定义一些全局任务池,强制执行所有异步任务以在其中运行,然后在检查断言之前等待池中的所有任务完成。 这样的任务池可以采用ExecutorServiceThreadGroup或任何数量的其他forms。 它们最终都做同样的事情,但可能或多或少地适合你的环境 – 主要的一点是你必须明确地给调用者访问新创建的线程,这是某种方式。

由于您的线程似乎执行不同的操作,您可以使用CountDownLatch来解决您的问题。

在主线程中声明CountDownLatch并将此latch对象传递给其他线程。 在主线程中使用await()并在其他线程中减少latch。

在主线程中:(第一个主题)

 CountDownLatch latch = new CountDownLatch(2); /* Create Second thread and pass the latch. Pass the same latch from second thread to third thread when you are creating third thread */ try { latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } 

将此锁存器传递给第二个和第三个线程,并在这些线程中使用倒计时

在第二和第三线程中,

 try { // add your business logic ie run() method implementation latch.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } 

看看这篇文章可以更好地理解。

ExecutorService invokeAll ()API是另一种更好的解决方案。

您无法对设备未提供的function进行unit testing。

你说要validationmethodToTest() 最终设置Clazz.i=2 ,但“最终”是什么意思? 您的methodToTest()函数不会为其调用者提供任何方式来了解何时设置了Clazz.i 。 您很难确定如何测试该function的原因是因为您的模块没有提供该function。

这可能是您阅读测试驱动开发(TDD)的好时机。 这是您首先编写测试的地方,然后编写使测试通过的代码。 首先编写测试可以帮助您更清楚地了解您希望模块执行的操作。

它还有一个附带好处:如果您练习严格的 TDD(即,如果您从未编写任何模块代码, 除了进行测试通过),那么您的测试覆盖率将是100%。

而且,这会带来另一个好处:如果你有100%的测试覆盖率,那么你可以毫无畏惧地进行重构,因为如果你破坏任何东西,你的unit testing就会告诉你。