当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《如何知道其他线程是否已完成?》经验,为你挑选了3个好方法。

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

如何在每个线程执行完毕后收到通知?

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



1> Eddie..:

您可以通过多种方式执行此操作:

    在主线程中使用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 Set listeners
                   = 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的代码,这非常简单.


我更新了我的答案以添加代码示例.
但是使用这种方法,notifiyListeners在run()内部被调用,因此它将被调用insisde线程,并且那里也会进一步调用,这不是这样吗?

2> Boris Pavlov..:

使用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个进入它,包括执行的主线程,主线程将仅在所有其他线程完成下载后继续执行.



3> broc.seib..:

你应该真正喜欢使用的解决方案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如果您的项目允许,您应该真正使用它.

推荐阅读
小妖694_807
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有