如何知道其他线程是否完成?
我有一个名为StartDownload()
的方法的对象,启动三个线程。
每个线程执行完成后如何获得通知?
有没有办法知道一个(或全部)线程是否已经完成或仍在执行?
有很多方法可以做到这一点:
- 在你的主线程中使用Thread.join() ,以阻塞的方式等待每个线程完成,或者
- 以轮询方式检查Thread.isAlive() – 通常不鼓励 – 等到每个线程完成或者
- 对于每个有问题的线程, 非正统的 ,调用setUncaughtExceptionHandler来调用你的对象的方法,并编程每个线程抛出一个未捕获的exception,当它完成,或
- 使用来自java.util.concurrent的locking或同步器或机制,或
- 更正统的做法是,在主线程中创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
并轮询ThreadGroup
的activeCount()
,并等待它到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()方法。