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

2022-08-31 08:08:03

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

当每个线程完成执行时,如何收到通知?

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


答案 1

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

  1. 在主线程中使用 Thread.join() 以阻塞方式等待每个线程完成,或者
  2. 以轮询方式检查 Thread.isAlive() - 通常不鼓励这样做 - 等到每个线程完成,或者
  3. 非正统的,对于所讨论的每个线程,调用 setUncaughtExceptionHandler 来调用对象中的方法,并对每个 Thread 进行编程,以便在完成时引发一个未捕获的异常,或者
  4. 使用 java.util.concurrent 中的锁、同步器或机制,或者
  5. 更正更直接的做法是,在主线程中创建一个侦听器,然后对每个线程进行编程,以告诉侦听器它们已完成。

如何实现想法#5?好吧,一种方法是首先创建一个接口:

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

然后创建以下类:

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();
}

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

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

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

然后,当每个 Thread 退出时,将使用刚刚完成(或崩溃)的 Thread 实例调用您的方法。notifyOfThreadComplete

请注意,更好的是 to 而不是 for,因为在新代码中通常不鼓励扩展 Thread。但是我正在编码你的问题。如果将类更改为实现,则必须更改一些管理 Threads 的代码,这非常简单。implements Runnableextends ThreadNotifyingThreadNotifyingThreadRunnable


答案 2

使用循环巴里尔的解决方案

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() 正在执行)

标签 1 - 第 n 个 下载线程进入等候室

标签3 - NUMBER_OF_DOWNLOADING_THREADS已进入候诊室。执行的主线程释放它们,使其在或多或少相同的时间开始执行下载作业

标签 4 - 执行的主线程进入等候室。这是代码中“最难理解”的部分。哪个线程将第二次进入等候室并不重要。重要的是,无论最后进入房间的线程如何,都要确保所有其他下载线程都已完成其下载作业。

标签 2 - 第 n 个 DownloadingThread 已完成下载作业并进入等候室。如果它是最后一个,即已经NUMBER_OF_DOWNLOADING_THREADS输入它,包括执行的主线程,则只有当所有其他线程都完成下载时,主线程才会继续执行。