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

时间:2016-01-29 02:33:03

标签: java multithreading junit executorservice countdownlatch

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

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

在我的单元测试中,我想测试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

如何使第一个线程等待它启动的线程以及该线程启动的任何线程

3 个答案:

答案 0 :(得分:5)

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

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

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

答案 1 :(得分:3)

由于您的主题似乎在执行不同的操作,因此您可以使用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 i.e. run() method implementation
    latch.countDown();
} catch (InterruptedException e) {
    e.printStackTrace();
}

看看这个article以便更好地理解。

ExecutorService invokeAll()API是其他首选解决方案。

答案 2 :(得分:0)

您无法对设备未提供的功能进行单元测试。

您说要验证methodToTest() 最终设置Clazz.i=2,但“最终”是什么意思?您的methodToTest()函数未向其调用方提供任何方式来了解何时设置Clazz.i。您很难弄清楚如何测试该功能的原因是因为您的模块没有提供该功能。

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

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

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