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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/702415/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 18:31:39  来源:igfitidea点击:

How to know if other threads have finished?

javamultithreading

提问by Ricardo Felgueiras

I have an object with a method named StartDownload(), that starts three threads.

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

How do I get a notification when each thread has finished executing?

当每个线程完成执行时如何获得通知?

Is there a way to know if one (or all) of the thread is finished or is still executing?

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

采纳答案by Eddie

There are a number of ways you can do this:

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

  1. Use Thread.join()in your main thread to wait in a blocking fashion for each Thread to complete, or
  2. Check Thread.isAlive()in a polling fashion -- generally discouraged -- to wait until each Thread has completed, or
  3. Unorthodox, for each Thread in question, call setUncaughtExceptionHandlerto call a method in your object, and program each Thread to throw an uncaught Exception when it completes, or
  4. Use locks or synchronizers or mechanisms from java.util.concurrent, or
  5. More orthodox, create a listener in your main Thread, and then program each of your Threads to tell the listener that they have completed.
  1. 在主线程中使用Thread.join()以阻塞方式等待每个线程完成,或者
  2. 以轮询方式检查Thread.isAlive()- 通常不鼓励 - 等待每个线程完成,或者
  3. 非正统,对于有问题的每个线程,调用setUncaughtExceptionHandler来调用对象中的方法,并对每个线程进行编程,使其在完成时抛出未捕获的异常,或者
  4. 使用来自java.util.concurrent 的锁或同步器或机制,或
  5. 更正统,在你的主线程中创建一个监听器,然后对你的每个线程进行编程,告诉监听器他们已经完成了。

How to implement Idea #5? Well, one way is to first create an interface:

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

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

then create the following class:

然后创建以下类:

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

and then each of your Threads will extend NotifyingThreadand instead of implementing run()it will implement doRun(). Thus when they complete, they will automatically notify anyone waiting for notification.

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

Finally, in your main class -- the one that starts all the Threads (or at least the object waiting for notification) -- modify that class to implement ThreadCompleteListenerand immediately after creating each Thread add itself to the list of listeners:

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

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

then, as each Thread exits, your notifyOfThreadCompletemethod will be invoked with the Thread instance that just completed (or crashed).

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

Note that better would be to implements Runnablerather than extends Threadfor NotifyingThreadas extending Thread is usually discouraged in new code. But I'm coding to your question. If you change the NotifyingThreadclass to implement Runnablethen you have to change some of your code that manages Threads, which is pretty straightforward to do.

请注意,最好是 toimplements Runnable而不是extends ThreadforNotifyingThread因为在新代码中通常不鼓励扩展 Thread 。但我正在为你的问题编码。如果您更改NotifyingThread要实现的类,Runnable那么您必须更改一些管理线程的代码,这非常简单。

回答by Jonathan Allen

Do you want to wait for them to finish? If so, use the Join method.

你想等他们完成吗?如果是这样,请使用 Join 方法。

There is also the isAlive property if you just want to check it.

如果您只想检查它,还有 isAlive 属性。

回答by Don Kirkby

Look at the Java documentation for the Thread class. You can check the thread's state. If you put the three threads in member variables, then all three threads can read each other's states.

查看 Thread 类的 Java 文档。您可以检查线程的状态。如果将三个线程放在成员变量中,那么三个线程都可以读取彼此的状态。

You have to be a bit careful, though, because you can cause race conditions between the threads. Just try to avoid complicated logic based on the state of the other threads. Definitely avoid multiple threads writing to the same variables.

但是,您必须小心一点,因为您可能会导致线程之间出现竞争条件。尽量避免基于其他线程状态的复杂逻辑。绝对避免多个线程写入相同的变量。

回答by Miquel

You can interrogate the thread instance with getState() which returns an instance of Thread.State enumeration with one of the following values:

您可以使用 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.

However I think it would be a better design to have a master thread which waits for the 3 children to finish, the master would then continue execution when the other 3 have finished.

但是,我认为拥有一个等待 3 个孩子完成的主线程会是一个更好的设计,然后主线程会在其他 3 个完成后继续执行。

回答by digitaljoel

I would suggest looking at the javadoc for Threadclass.

我建议查看Thread类的 javadoc 。

You have multiple mechanisms for thread manipulation.

您有多种线程操作机制。

  • Your main thread could join()the three threads serially, and would then not proceed until all three are done.

  • Poll the thread state of the spawned threads at intervals.

  • Put all of the spawned threads into a separate ThreadGroupand poll the activeCount()on the ThreadGroupand wait for it to get to 0.

  • Setup a custom callback or listener type of interface for inter-thread communication.

  • 您的主线程可以join()连续三个线程,然后在三个线程都完成之前不会继续。

  • 每隔一段时间轮询产生的线程的线程状态。

  • 把所有的衍生线程到一个单独的ThreadGroup和轮询activeCount()ThreadGroup,等待它去0。

  • 为线程间通信设置自定义回调或侦听器类型的接口。

I'm sure there are plenty of other ways I'm still missing.

我确定还有很多其他方法我仍然缺少。

回答by Boris Pavlovi?

Solution using CyclicBarrier

使用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- cyclic barrier is created with number of parties equal to the number of executing threads plus one for the main thread of execution (in which startDownload() is being executed)

label0- 创建循环屏障,参与方的数量等于执行线程的数量加上主执行线程的数量(其中正在执行 startDownload())

label 1- n-th DownloadingThread enters the waiting room

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

label 3- NUMBER_OF_DOWNLOADING_THREADS have entered the waiting room. Main thread of execution releases them to start doing their downloading jobs in more or less the same time

标签 3- NUMBER_OF_DOWNLOADING_THREADS 已进入候诊室。主执行线程释放它们以或多或少同时开始执行下载工作

label 4- main thread of execution enters the waiting room. This is the 'trickiest' part of the code to understand. It doesn't matter which thread will enter the waiting room for the second time. It is important that whatever thread enters the room last ensures that all the other downloading threads have finished their downloading jobs.

标签 4- 执行的主线程进入等候室。这是代码中最难理解的部分。哪个线程第二次进入等候室并不重要。最后进入房间的线程确保所有其他下载线程都完成了它们的下载工作,这一点很重要。

label 2- n-th DownloadingThread has finished its downloading job and enters the waiting room. If it is the last one i.e. already NUMBER_OF_DOWNLOADING_THREADS have entered it, including the main thread of execution, main thread will continue its execution only when all the other threads have finished downloading.

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

回答by akarnokd

You could also use SwingWorker, which has built-in property change support. See addPropertyChangeListener()or the get()method for a state change listener example.

您还可以使用 SwingWorker,它具有内置的属性更改支持。有关状态更改侦听器示例,请参阅addPropertyChangeListener()get()方法。

回答by J Gitter

You could also use the Executorsobject to create an ExecutorServicethread pool. Then use the invokeAllmethod to run each of your threads and retrieve Futures. This will block until all have finished execution. Your other option would be to execute each one using the pool and then call awaitTerminationto block until the pool is finished executing. Just be sure to call shutdown() when you're done adding tasks.

您还可以使用该Executors对象来创建一个 ExecutorService线程池。然后使用该invokeAll方法运行每个线程并检索 Futures。这将阻塞,直到所有都完成执行。您的另一种选择是使用池执行每一个,然后调用awaitTermination阻塞直到池完成执行。shutdown完成添加任务后,请务必调用()。

回答by broc.seib

You should reallyprefer a solution that uses java.util.concurrent. Find and read Josh Bloch and/or Brian Goetz on the topic.

真的应该更喜欢使用java.util.concurrent. 查找并阅读有关该主题的 Josh Bloch 和/或 Brian Goetz。

If you are not using java.util.concurrent.*and are taking responsibility for using Threads directly, then you should probably use join()to know when a thread is done. Here is a super simple Callback mechanism. First extend the Runnableinterface to have a callback:

如果您没有java.util.concurrent.*直接使用线程并且负责直接使用线程,那么您可能应该使用它join()来了解线程何时完成。这里有一个超级简单的回调机制。首先扩展Runnable接口有一个回调:

public interface CallbackRunnable extends Runnable {
    public void callback();
}

Then make an Executor that will execute your runnable and call you back when it is done.

然后创建一个 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();
        }
    }

}

The other sort-of obvious thing to add to your CallbackRunnableinterface is a means to handle any exceptions, so maybe put a public void uncaughtException(Throwable e);line in there and in your executor, install a Thread.UncaughtExceptionHandler to send you to that interface method.

要添加到您的CallbackRunnable界面中的另一种显而易见的事情是一种处理任何异常的方法,因此可以public void uncaughtException(Throwable e);在其中放置一行并在您的执行程序中安装一个 Thread.UncaughtExceptionHandler 以将您发送到该接口方法。

But doing all that really starts to smell like java.util.concurrent.Callable. You should really look at using java.util.concurrentif your project permits it.

但做这一切真的开始闻起来像java.util.concurrent.Callablejava.util.concurrent如果您的项目允许,您应该真正考虑使用。

回答by Ravindra babu

Many things have been changed in last 6 years on multi-threading front.

在过去的 6 年中,多线程方面的许多事情都发生了变化。

Instead of using join()and lock API, you can use

除了使用join()和锁定 API,您还可以使用

1.ExecutorServiceinvokeAll()API

1. ExecutorServiceinvokeAll()API

Executes the given tasks, returning a list of Futures holding their status and results when all complete.

执行给定的任务,返回一个 Futures 列表,在所有完成时保存它们的状态和结果。

2.CountDownLatch

2.倒计时锁存器

A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.

A CountDownLatchis initialized with a given count. The await methods block until the current count reaches zero due to invocations of the countDown()method, after which all waiting threads are released and any subsequent invocations of await return immediately. This is a one-shot phenomenon -- the count cannot be reset. If you need a version that resets the count, consider using a CyclicBarrier.

一种同步辅助,允许一个或多个线程等待,直到在其他线程中执行的一组操作完成。

ACountDownLatch用给定的计数初始化。由于调用了该countDown()方法,await 方法会阻塞直到当前计数达到零,然后释放所有等待线程,并且任何后续的 await 调用都会立即返回。这是一种一次性现象——计数无法重置。如果您需要重置计数的版本,请考虑使用 CyclicBarrier。

3.ForkJoinPoolor newWorkStealingPool()in Executorsis other way

3. ForkJoinPoolnewWorkStealingPool()Executors是其他方式

4.Iterate through all Futuretasks from submit on ExecutorServiceand check the status with blocking call get()on Futureobject

4.遍历Future从提交开始的所有任务,ExecutorService并通过get()Future对象的阻塞调用来检查状态

Have a look at related SE questions:

看看相关的 SE 问题:

How to wait for a thread that spawns it's own thread?

如何等待一个线程产生它自己的线程?

Executors: How to synchronously wait until all tasks have finished if tasks are created recursively?

Executors:如果任务是递归创建的,如何同步等待所有任务完成?