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
How to know if other threads have finished?
提问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:
您可以通过多种方式执行此操作:
- Use Thread.join()in your main thread to wait in a blocking fashion for each Thread to complete, or
- Check Thread.isAlive()in a polling fashion -- generally discouraged -- to wait until each Thread has completed, or
- 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
- Use locks or synchronizers or mechanisms from java.util.concurrent, or
- More orthodox, create a listener in your main Thread, and then program each of your Threads to tell the listener that they have completed.
- 在主线程中使用Thread.join()以阻塞方式等待每个线程完成,或者
- 以轮询方式检查Thread.isAlive()- 通常不鼓励 - 等待每个线程完成,或者
- 非正统,对于有问题的每个线程,调用setUncaughtExceptionHandler来调用对象中的方法,并对每个线程进行编程,使其在完成时抛出未捕获的异常,或者
- 使用来自java.util.concurrent 的锁或同步器或机制,或
- 更正统,在你的主线程中创建一个监听器,然后对你的每个线程进行编程,告诉监听器他们已经完成了。
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 NotifyingThread
and 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 ThreadCompleteListener
and 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 notifyOfThreadComplete
method will be invoked with the Thread instance that just completed (or crashed).
然后,当每个 Thread 退出时,您的notifyOfThreadComplete
方法将使用刚刚完成(或崩溃)的 Thread 实例调用。
Note that better would be to implements Runnable
rather than extends Thread
for NotifyingThread
as extending Thread is usually discouraged in new code. But I'm coding to your question. If you change the NotifyingThread
class to implement Runnable
then you have to change some of your code that manages Threads, which is pretty straightforward to do.
请注意,最好是 toimplements Runnable
而不是extends Thread
forNotifyingThread
因为在新代码中通常不鼓励扩展 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
ThreadGroup
and poll theactiveCount()
on theThreadGroup
and 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 Executors
object to create an ExecutorServicethread pool. Then use the invokeAll
method 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 awaitTermination
to 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 Runnable
interface 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 CallbackRunnable
interface 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.concurrent
if your project permits it.
但做这一切真的开始闻起来像java.util.concurrent.Callable
。java.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.倒计时锁存器
A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.
A
CountDownLatch
is initialized with a given count. The await methods block until the current count reaches zero due to invocations of thecountDown()
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.
一种同步辅助,允许一个或多个线程等待,直到在其他线程中执行的一组操作完成。
A
CountDownLatch
用给定的计数初始化。由于调用了该countDown()
方法,await 方法会阻塞直到当前计数达到零,然后释放所有等待线程,并且任何后续的 await 调用都会立即返回。这是一种一次性现象——计数无法重置。如果您需要重置计数的版本,请考虑使用 CyclicBarrier。
3.ForkJoinPoolor newWorkStealingPool()
in Executorsis other way
3. ForkJoinPool或 newWorkStealingPool()
在Executors是其他方式
4.Iterate through all Future
tasks from submit on ExecutorService
and check the status with blocking call get()
on Future
object
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?