我有一个名为的方法的对象StartDownload()
,它启动三个线程.
如何在每个线程执行完毕后收到通知?
有没有办法知道一个(或全部)线程是完成还是仍在执行?
您可以通过多种方式执行此操作:
在主线程中使用Thread.join()以阻塞方式等待每个Thread完成,或者
以轮询方式检查Thread.isAlive() - 通常不鼓励 - 等待每个线程完成,或者
非正统的,对于每个有问题的线程,调用setUncaughtExceptionHandler来调用对象中的方法,并对每个Thread进行编程,以便在完成时抛出未被捕获的异常,或者
使用java.util.concurrent中的锁或同步器或机制,或
更正统的是,在主线程中创建一个监听器,然后对每个线程进行编程,告诉监听器它们已经完成.
如何实现Idea#5?好吧,一种方法是先创建一个界面:
public interface ThreadCompleteListener { void notifyOfThreadComplete(final Thread thread); }
然后创建以下类:
public abstract class NotifyingThread extends Thread { private final Setlisteners = new CopyOnWriteArraySet (); 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()
.因此,当他们完成时,他们会自动通知任何等待通知的人.
最后,在您的主类 - 启动所有线程(或至少等待通知的对象)的类 - 修改该类,implement ThreadCompleteListener
并在创建每个线程后立即将其自身添加到侦听器列表:
NotifyingThread thread1 = new OneOfYourThreads(); thread1.addListener(this); // add ourselves as a listener thread1.start(); // Start the Thread
然后,当每个Thread退出时,notifyOfThreadComplete
将使用刚刚完成(或崩溃)的Thread实例调用您的方法.
需要注意的是更好的将implements Runnable
,而不是extends Thread
用于NotifyingThread
为延长线为新的代码通常气馁.但我正在编写你的问题.如果你改变NotifyingThread
要实现的类,Runnable
那么你必须改变一些管理Threads的代码,这非常简单.
使用CyclicBarrier的解决方案
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 void 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 - 创建循环屏障,其中,当事方的数量等于执行线程的数量加上一个用于执行的主线程(正在执行startDownload())
label 1 - n-DownloadingThread进入候诊室
标签3 - NUMBER_OF_DOWNLOADING_THREADS已进入候诊室.主要的执行线程释放它们以开始在或多或少的同时开始下载作业
标签4 - 主要执行线程进入候补室.这是要理解的代码中"最棘手"的部分.哪个线程第二次进入候诊室并不重要.重要的是,无论什么线程进入房间最后确保所有其他下载线程已完成其下载作业.
label 2 - n-DownloadingThread已完成下载工作并进入候补室.如果它是最后一个,即已经有NUMBER_OF_DOWNLOADING_THREADS个进入它,包括执行的主线程,主线程将仅在所有其他线程完成下载后继续执行.
你应该真正喜欢使用的解决方案java.util.concurrent
.查找和阅读Josh Bloch和/或Brian Goetz的主题.
如果您没有使用java.util.concurrent.*
并且直接负责使用Threads,那么您应该join()
知道线程何时完成.这是一个超级简单的回调机制.首先扩展Runnable
接口以进行回调:
public interface CallbackRunnable extends Runnable { public void callback(); }
然后创建一个将执行runnable的Executor,并在完成后回拨给你.
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
接口的另一种显而易见的事情是处理任何异常的方法,因此可能public void uncaughtException(Throwable e);
在那里和执行程序中添加一行,安装Thread.UncaughtExceptionHandler以将您发送到该接口方法.
但做这一切真的开始闻起来像java.util.concurrent.Callable
.java.util.concurrent
如果您的项目允许,您应该真正使用它.