我有一个名为的方法的对象,该方法StartDownload()
启动三个线程。
每个线程执行完毕后,如何获得通知?
有没有办法知道一个(或全部)线程是否已完成或仍在执行?
我有一个名为的方法的对象,该方法StartDownload()
启动三个线程。
每个线程执行完毕后,如何获得通知?
有没有办法知道一个(或全部)线程是否已完成或仍在执行?
Answers:
您可以通过多种方式执行此操作:
如何实施想法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();
}
然后您的每个线程都将扩展NotifyingThread
,而不是实现run()
它将实现doRun()
。因此,完成后,他们将自动通知任何等待通知的人。
最后,在您的主类(启动所有线程(或至少是等待通知的对象)的主类)中,将该类修改为implement ThreadCompleteListener
创建每个线程后立即将其自身添加到侦听器列表中:
NotifyingThread thread1 = new OneOfYourThreads();
thread1.addListener(this); // add ourselves as a listener
thread1.start(); // Start the Thread
然后,在每个线程退出时,notifyOfThreadComplete
将使用刚刚完成(或崩溃)的Thread实例调用您的方法。
需要注意的是更好的将implements Runnable
,而不是extends Thread
用于NotifyingThread
为延长线为新的代码通常气馁。但是,我正在编码您的问题。如果您更改NotifyingThread
要实现的类,Runnable
则必须更改一些管理线程的代码,这非常简单。
notify
不是在run
方法之后而是在方法之后调用方法。
使用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())
标签1-第n个DownloadingThread进入等候室
标签3 -NUMBER_OF_DOWNLOADING_THREADS个进入等候室。执行的主线程释放他们开始或多或少同时开始下载工作
标签4-执行的主线程进入候诊室。这是代码中“最复杂”的部分。哪个线程第二次进入等候室无关紧要。重要的是,无论哪个线程最后进入房间,都必须确保所有其他下载线程都已完成其下载工作。
标签2-第n个DownloadingThread已完成其下载工作,并进入等候室。如果它是最后一个,即已经输入了NUMBER_OF_DOWNLOADING_THREADS,包括执行的主线程,则只有在所有其他线程都已完成下载后,主线程才会继续执行。
您确实应该更喜欢使用的解决方案java.util.concurrent
。查找并阅读有关该主题的Josh Bloch和/或Brian Goetz。
如果您没有使用java.util.concurrent.*
线程并直接负责使用线程,那么您应该join()
知道线程何时完成。这是一个超级简单的回调机制。首先扩展Runnable
接口以具有回调:
public interface CallbackRunnable extends Runnable {
public void callback();
}
然后使执行器执行您的可运行程序,并在完成时回叫您。
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
如果项目允许,您应该真正考虑使用。
runner.join()
然后从中获取任何代码有关,因为您知道线程已完成。仅仅是将代码定义为可运行对象的属性,这样对于不同的可运行对象可以有不同的东西吗?
runner.join()
是最直接的等待方式。我以为OP不想阻止其主调用线程,因为他们要求每次下载都要“通知”,该下载可以以任何顺序完成。这提供了一种异步通知的方法。
您要等待他们完成吗?如果是这样,请使用Join方法。
如果您只想检查它,则还有isAlive属性。
Thread
to start
,该线程执行哪个操作,但是调用立即返回。isAlive
应该是一个简单的标志测试,但是当我用Google搜索它时,方法是native
。
您可以使用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.
但是,我认为最好有一个主线程来等待3个子线程完成,然后在其他3个子线程完成后再继续执行。
您也可以使用该Executors
对象创建 ExecutorService线程池。然后使用该invokeAll
方法来运行每个线程并检索Future。这将阻塞,直到所有执行完毕。您的另一个选择是使用该池执行每个执行,然后调用awaitTermination
阻塞,直到该池执行完毕。shutdown
完成添加任务后,只需确保调用()。
在过去的6年中,多线程方面发生了许多变化。
除了使用join()
和锁定API外,您还可以使用
1. ExecutorService invokeAll()
API
执行给定的任务,并在所有任务完成时返回保存其状态和结果的期货列表。
一种同步帮助,它允许一个或多个线程等待,直到在其他线程中执行的一组操作完成为止。
CountDownLatch
用给定的计数初始化A。由于该countDown()
方法的调用,直到当前计数达到零为止,await方法将阻塞,此后所有释放的线程将被释放,任何随后的await调用将立即返回。这是一种一次性现象-无法重置计数。如果需要用于重置计数的版本,请考虑使用CyclicBarrier。
3. ForkJoinPool或 newWorkStealingPool()
在执行人是另一种方式
4,遍历Future
提交后的所有任务,ExecutorService
并通过阻止对象调用get()
来检查状态Future
看看相关的SE问题:
我建议查看Thread 类的javadoc 。
您有多种线程处理机制。
您的主线程可以join()
依次执行三个线程,然后在所有三个线程都完成之后再继续进行。
间隔轮询生成的线程的线程状态。
将所有生成的线程放在单独的位置,ThreadGroup
并在activeCount()
上轮询ThreadGroup
并等待其变为0。
设置用于线程间通信的自定义回调或侦听器类型的接口。
我敢肯定,还有许多其他方式我仍会丢失。
这是一个简单,简短,易于理解的解决方案,非常适合我。当另一个线程结束时,我需要绘制屏幕。但是不能,因为主线程可以控制屏幕。所以:
(1)我创建了全局变量:boolean end1 = false;
线程在结束时将其设置为true。它在主线程中通过“ postDelayed”循环进行拾取,并在其中进行响应。
(2)我的线程包含:
void myThread() {
end1 = false;
new CountDownTimer(((60000, 1000) { // milliseconds for onFinish, onTick
public void onFinish()
{
// do stuff here once at end of time.
end1 = true; // signal that the thread has ended.
}
public void onTick(long millisUntilFinished)
{
// do stuff here repeatedly.
}
}.start();
}
(3)幸运的是,“ postDelayed”在主线程中运行,因此每秒可以检查另一个线程。当另一个线程结束时,这可以开始我们接下来要做的任何事情。
Handler h1 = new Handler();
private void checkThread() {
h1.postDelayed(new Runnable() {
public void run() {
if (end1)
// resond to the second thread ending here.
else
h1.postDelayed(this, 1000);
}
}, 1000);
}
(4)最后,通过调用以下代码来开始运行整个代码:
void startThread()
{
myThread();
checkThread();
}
我猜最简单的方法是使用ThreadPoolExecutor
类。
挂钩方法
这个类提供保护的重写
beforeExecute(java.lang.Thread, java.lang.Runnable)
和afterExecute(java.lang.Runnable, java.lang.Throwable)
之前和每个任务的执行之后被调用的方法。这些可以用来操纵执行环境。例如,重新初始化ThreadLocals,收集统计信息或添加日志条目。此外,terminated()
一旦执行程序完全终止,可以重写方法来执行需要执行的任何特殊处理。
这正是我们所需要的。afterExecute()
在每个线程完成后,我们将重写以获取回调,并且terminated()
在所有线程完成时将进行重写 以获取回调。
所以这是你应该做的
创建执行程序:
private ThreadPoolExecutor executor;
private int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();
private void initExecutor() {
executor = new ThreadPoolExecutor(
NUMBER_OF_CORES * 2, //core pool size
NUMBER_OF_CORES * 2, //max pool size
60L, //keep aive time
TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>()
) {
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
//Yet another thread is finished:
informUiAboutProgress(executor.getCompletedTaskCount(), listOfUrisToProcess.size());
}
}
};
@Override
protected void terminated() {
super.terminated();
informUiThatWeAreDone();
}
}
并启动您的线程:
private void startTheWork(){
for (Uri uri : listOfUrisToProcess) {
executor.execute(new Runnable() {
@Override
public void run() {
doSomeHeavyWork(uri);
}
});
}
executor.shutdown(); //call it when you won't add jobs anymore
}
内部方法informUiThatWeAreDone();
可以完成所有线程后所需执行的任何操作,例如,更新UI。
注意:不要忘记使用synchronized
方法,因为您并行进行工作,如果决定synchronized
从另一个方法调用方法,请务必小心synchronized
方法!这通常会导致死锁
希望这可以帮助!
您也可以使用SwingWorker,它具有内置的属性更改支持。有关状态更改侦听器的示例,请参见addPropertyChangeListener()或get()方法。