如何在Java中等待线程的启动

6
我在当前的Android测试中遇到了一个小的竞态条件。我想要的是:
  1. T1:启动线程T2
  2. T2:做一些事情
  3. T1:与T2合并
其中步骤1和3是Android生命周期事件。但由于在仪器测试中一切都发生得非常快,所以我得到了以下结果:
  1. T1:启动线程T2
  2. T1:与T2合并(结果是无操作)
  3. T2:做一些事情
当然,我可以添加一些延时来获得所需的行为,但我想知道是否有更好的方法。例如,是否有一种方法可以确保刚刚启动的线程已经真正启动,并且不再等待启动而坐在某个调度队列中。
(Andy boy,我想念Ada基于会合的多任务处理)
回答mat的问题:
  if (this.thread != null && this.thread.isAlive ())
  {
     this.stop.set (true);

     try
     {
        this.thread.join (1000);
     }
     catch (final InterruptedException Exception)
     {
        android.util.Log.w (Actor.TAG, "Thread did not want to join.", Exception);
     } // try
  } // if

就像我之前说的一样:当线程还没有开始时,它是无操作的。


这句话的意思是 Thread.join() 等待被加入的线程结束(除非你设置了非常短的超时时间)。你确定错误不是出在其他地方吗? - Mat
问题不在于单元测试的速度,而是线程调度的方式。 - jefflunt
@Mat:我添加了一段代码示例来回答那个问题。 - Martin
@Martin:你在那里测试 thread != null 是为什么?你确定你正在进入那个 if 语句吗?你的第二个线程在开始处理之前是否检查了 stop.set(true) 设置的任何内容? - Mat
@Martin - 看一眼 - Awaitility .. 可能会有用。 - Premraj
好的,这是Android,一切都是“控制反转”,步骤1和3是不同的生命周期事件。在我的仪器测试中,我知道onStart在onDestroy之前被调用,而且this.tread不会为空(也通过查看日志文件进行了确认)。然而,在最终应用程序中,可能会出现Service被销毁而从未启动的情况。 - Martin
2个回答

16

通常我会使用 CountDownLatch,例如在这个答案中介绍了如何测试异步进程。

如果您想同步许多线程的启动,也可以使用CyclicBarrier


谢谢!CountDownLatch和CyclicBarrier正是我正在寻找的。CountDownLatch用于当前问题。而CyclicBarrier则提供了Ada会合的基本功能,我一直认为这是最好的多任务范例之一,因此我会记在心里以备将来使用。 - Martin

0
Martin,看了你的代码,我感觉你可能没有按照Thread类设计的方式使用它。特别是测试另一个线程是否存活似乎是一种反模式。在大多数实际情况下,您可以从代码中省略this.thread.isAlive()条件,程序仍然可以正常工作。
看起来你正在尝试让两个线程(应该做两件不同的事情)运行相同的代码,并且你使用逻辑条件(如this.thread != null)来决定哪个线程当前正在运行。
通常,您会编写两个类,每个类都扩展Thread并实现一个run()方法。每个run()方法实现单个线程的逻辑。然后,您将从第一个线程启动第二个线程,并在该第二个线程上调用join()以等待其完成。
public class SecondThread extends Thread {
  public void run() {
    ...
  }      
}

public class FirstThread extends Thread {
  public void run() {
    // Only FirstThread is running
    ...

    SecondThread st = new SecondThread();
    st.start();

    // Now both threads are running
    ...

    st.join(); // Wait for SecondThread to complete

    // Only FirstThread is running
    ...

  }                
}

1
注意:从面向对象编程的角度来看,最好实现Runnable而不是直接扩展Thread本身。 - Voo
抱歉,我应该强调一下我所说的是关于Android仪器测试的,“控制反转”是其中的一个要点。第一步是onStart生命周期事件,第三步是onDestroy。this.thread != null之所以存在是因为存在一些不平衡(onCreate、onStart、onDestroy但没有onStop),并且可能会在没有onStart的情况下调用onDestroy。 - Martin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接