Ric*_*ras 121 java multithreading
我有一个名为的方法的对象StartDownload()
,它启动三个线程.
如何在每个线程执行完毕后收到通知?
有没有办法知道一个(或全部)线程是完成还是仍在执行?
Edd*_*die 225
您可以通过多种方式执行此操作:
如何实现Idea#5?好吧,一种方法是先创建一个界面:
public interface ThreadCompleteListener {
void notifyOfThreadComplete(final Thread thread);
}
Run Code Online (Sandbox Code Playgroud)
然后创建以下类:
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();
}
Run Code Online (Sandbox Code Playgroud)
然后每个线程都将扩展NotifyingThread
,而不是实现run()
它将实现doRun()
.因此,当他们完成时,他们会自动通知任何等待通知的人.
最后,在您的主类 - 启动所有线程(或至少等待通知的对象)的类 - 修改该类,implement ThreadCompleteListener
并在创建每个线程后立即将其自身添加到侦听器列表:
NotifyingThread thread1 = new OneOfYourThreads();
thread1.addListener(this); // add ourselves as a listener
thread1.start(); // Start the Thread
Run Code Online (Sandbox Code Playgroud)
然后,当每个Thread退出时,notifyOfThreadComplete
将使用刚刚完成(或崩溃)的Thread实例调用您的方法.
需要注意的是更好的将implements Runnable
,而不是extends Thread
用于NotifyingThread
为延长线为新的代码通常气馁.但我正在编写你的问题.如果你改变NotifyingThread
要实现的类,Runnable
那么你必须改变一些管理Threads的代码,这非常简单.
Bor*_*vić 13
使用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");
}
}
Run Code Online (Sandbox Code Playgroud)
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();
}
Run Code Online (Sandbox Code Playgroud)
然后创建一个将执行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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
添加到CallbackRunnable
接口的另一种显而易见的事情是处理任何异常的方法,因此可能public void uncaughtException(Throwable e);
在那里和执行程序中添加一行,安装Thread.UncaughtExceptionHandler以将您发送到该接口方法.
但做这一切真的开始闻起来像java.util.concurrent.Callable
.java.util.concurrent
如果您的项目允许,您应该真正使用它.