如何知道其他线程是否完成?

我有一个名为StartDownload()的方法的对象,启动三个线程。

每个线程执行完成后如何获得通知?

有没有办法知道一个(或全部)线程是否已经完成或仍在执行?

有很多方法可以做到这一点:

  1. 在你的主线程中使用Thread.join() ,以阻塞的方式等待每个线程完成,或者
  2. 以轮询方式检查Thread.isAlive() – 通常不鼓励 – 等到每个线程完成或者
  3. 对于每个有问题的线程, 非正统的 ,调用setUncaughtExceptionHandler来调用你的对象的方法,并编程每个线程抛出一个未捕获的exception,当它完成,或
  4. 使用来自java.util.concurrent的locking或同步器或机制,或
  5. 更正统的做法是,在主线程中创build一个监听器,然后对每个线程进行编程,告诉监听器他们已经完成了。

如何实施想法#5? 那么,一种方法是先创build一个接口:

 public interface ThreadCompleteListener { void notifyOfThreadComplete(final Thread thread); } 

然后创build以下类:

 public abstract class NotifyingThread extends Thread { private final Set<ThreadCompleteListener> listeners = new CopyOnWriteArraySet<ThreadCompleteListener>(); public final void addListener(final ThreadCompleteListener listener) { listeners.add(listener); } public final void removeListener(final ThreadCompleteListener listener) { listeners.remove(listener); } private final void notifyListeners() { for (ThreadCompleteListener listener : listeners) { listener.notifyOfThreadComplete(this); } } @Override public final void run() { try { doRun(); } finally { notifyListeners(); } } public abstract void doRun(); } 

然后每个线程将扩展NotifyingThread ,而不是实现run()它将实现doRun() 。 因此,当他们完成时,他们会自动通知等待通知的人。

最后,在你的主类 – 启动所有线程(或者至less是等待通知的对象)的类中 – 修改该类来implement ThreadCompleteListener并在创build每个线程之后立即将其自身添加到侦听器列表中:

 NotifyingThread thread1 = new OneOfYourThreads(); thread1.addListener(this); // add ourselves as a listener thread1.start(); // Start the Thread 

那么,当每个线程退出时,你的notifyOfThreadComplete方法将被调用,刚刚完成(或崩溃)的线程实例。

注意最好是implements Runnable而不是extends Thread for NotifyingThread因为在新代码中通常不鼓励扩展Thread。 但我正在编写你的问题。 如果您更改NotifyingThread类来实现Runnable则必须更改pipe理线程的一些代码,这非常简单。

使用CyclicBarrier解决scheme

 public class Downloader { private CyclicBarrier barrier; private final static int NUMBER_OF_DOWNLOADING_THREADS; private DownloadingThread extends Thread { private final String url; public DownloadingThread(String url) { super(); this.url = url; } @Override public void run() { barrier.await(); // label1 download(url); barrier.await(); // label2 } } public class startDownload() { // plus one for the main thread of execution barrier = new CyclicBarrier(NUMBER_OF_DOWNLOADING_THREADS + 1); // label0 for (int i = 0; i < NUMBER_OF_DOWNLOADING_THREADS; i++) { new DownloadingThread("http://www.flickr.com/someUser/pic" + i + ".jpg").start(); } barrier.await(); // label3 displayMessage("Please wait..."); barrier.await(); // label4 displayMessage("Finished"); } } 

label0 – 循环屏障的创build方法是执行线程的数量等于执行主线程的执行线程的数量(执行startDownload())。

标签1 – 第n个下载线程进入候车室

标签3 – NUMBER_OF_DOWNLOADING_THREADS已进入候诊室。 主线程的执行释放他们开始做或多或less同时下载作业

标签4 – 主执行线进入候车室。 这是要理解的代码中最“棘手”的部分。 哪个线程第二次进入候车室并不重要。 无论线程最后进入房间,确保所有其他下载线程完成其下载作业是非常重要的。

标签2 – n下载线程完成了下载工作并进入候车室。 如果是最后一个,也就是已经input了NUMBER_OF_DOWNLOADING_THREADS,包括执行的主线程,主线程只有在所有其他线程完成下载后才会继续执行。

你应该更喜欢使用java.util.concurrent的解决scheme。 find并阅读Josh Bloch和/或Brian Goetz的话题。

如果你不使用java.util.concurrent.*并且直接负责使用线程,那么你应该使用join()来知道线程何时完成。 这是一个超级简单的callback机制。 首先扩展Runnable接口以进行callback:

 public interface CallbackRunnable extends Runnable { public void callback(); } 

然后build立一个Executor来执行你的runnable,并在完成后给你回电。

 public class CallbackExecutor implements Executor { @Override public void execute(final Runnable r) { final Thread runner = new Thread(r); runner.start(); if ( r instanceof CallbackRunnable ) { // create a thread to perform the callback Thread callerbacker = new Thread(new Runnable() { @Override public void run() { try { // block until the running thread is done runner.join(); ((CallbackRunnable)r).callback(); } catch ( InterruptedException e ) { // someone doesn't want us running. ok, maybe we give up. } } }); callerbacker.start(); } } } 

另一种显而易见的事情是添加到你的CallbackRunnable接口是一种处理任何exception的方法,所以也许把一个public void uncaughtException(Throwable e); 在那里和在你的执行者,安装一个Thread.UncaughtExceptionHandler发送给你的接口方法。

但是这样做真的开始闻起来像java.util.concurrent.Callable 。 如果你的项目允许,你应该真的使用java.util.concurrent

你想等他们完成? 如果是这样,请使用Join方法。

如果你只是想检查它,还有isAlive属性。

您可以使用getState()来询问线程实例,该函数返回Thread.State枚举的一个实例,并使用以下值之一:

 * NEW A thread that has not yet started is in this state. * RUNNABLE A thread executing in the Java virtual machine is in this state. * BLOCKED A thread that is blocked waiting for a monitor lock is in this state. * WAITING A thread that is waiting indefinitely for another thread to perform a particular action is in this state. * TIMED_WAITING A thread that is waiting for another thread to perform an action for up to a specified waiting time is in this state. * TERMINATED A thread that has exited is in this state. 

不过,我认为有一个等待三个孩子完成的主线程将会是一个更好的devise,当另外三个孩子完成时,主人会继续执行。

我build议看看Thread类的javadoc。

您有多个线程处理机制。

  • 你的主线程可以连续地join()三个线程,然后直到所有三个线程完成。

  • 每隔一段时间轮询产生的线程的线程状态。

  • 把所有产生的线程放到一个单独的ThreadGroup并轮询ThreadGroupactiveCount() ,并等待它到0。

  • 为线程间通信设置自定义callback或侦听器types的接口。

我相信还有很多其他的方式,我仍然失踪。

您也可以使用Executors对象来创build一个ExecutorService线程池。 然后使用invokeAll方法来运行你的每个线程和检索期货。 这将阻塞,直到所有完成执行。 你的其他select是使用池执行每一个,然后调用awaitTermination来阻塞,直到池完成执行。 只要确定在完成添加任务时调用shutdown ()。

在multithreading方面,过去6年来许多事情已经发生了变化。

而不是使用join()和锁API,你可以使用

1. ExecutorService invokeAll() API

执行给定的任务,返回一份持有其状态和结果的期货清单。

2. CountDownLatch

同步协助,允许一个或多个线程等待,直到在其他线程中执行的一组操作完成。

CountDownLatch用给定的计数初始化。 await方法阻塞,直到当前计数由于调用countDown()方法而达到零,在此之后所有等待的线程被释放,并且任何后续的调用立即返回。 这是一次性现象 – 计数不能被重置。 如果您需要重置计数的版本,请考虑使用CyclicBarrier。

3.执行程序中的ForkJoinPool或newWorkStealingPool()是其他方法

4.通过ExecutorService提交所有Future任务,并通过在Future对象上阻塞调用get()来检查状态

看看相关的SE问题:

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

执行者:如何recursion创build任务,如何同步等待所有任务完成?

这是一个简单,简短,易于理解的解决scheme,对我来说是完美的。 另一个线程结束时,我需要画到屏幕上。 但不能因为主线程已经控制了屏幕。 所以:

(1)我创build了全局variables: boolean end1 = false; 结束时,线程将其设置为true。 这是通过“postDelayed”循环在主线程中拾取的,在那里响应。

(2)我的线程包含:

 void myThread() { end1 = false; new CountDownTimer(((60000, 1000) { // milliseconds for onFinish, onTick public void onFinish() { // do stuff here once at end of time. end1 = true; // signal that the thread has ended. } public void onTick(long millisUntilFinished) { // do stuff here repeatedly. } }.start(); } 

(3)幸运的是,“postDelayed”在主线程中运行,所以在每秒钟检查另一个线程的地方。 当另一个线程结束时,这可以开始我们接下来要做的任何事情。

 Handler h1 = new Handler(); private void checkThread() { h1.postDelayed(new Runnable() { public void run() { if (end1) // resond to the second thread ending here. else h1.postDelayed(this, 1000); } }, 1000); } 

(4)最后,通过调用以下代码开始在代码中的某处运行:

 void startThread() { myThread(); checkThread(); } 

看一下Thread类的java文档。 你可以检查线程的状态。 如果将三个线程放在成员variables中,那么所有三个线程都可以读取彼此的状态。 但是,你必须小心,因为你可能会在线程之间造成竞争状态。 只要尝试避免复杂的逻辑基于其他线程的状态。 绝对避免多个线程写入相同的variables。

您也可以使用SwingWorker,它具有内置的属性更改支持。 有关状态更改侦听器示例,请参阅addPropertyChangeListener()或get()方法。