获取线程的输出

您认为获得线程工作结果的最佳方法是什么? 想象一个线程做一些计算,你如何警告主程序计算完成?

对于一些称为“作业完成”的公共变量,你可以每隔X毫秒进行一次轮询,或者顺便说一下,然后你会收到比它们可用时更晚的结果…主代码会浪费时间等待它们。 另一方面,如果使用较低的X,CPU将浪费多次轮询。

那么,你怎么知道Thread或者一些Threads已经完成了他们的工作呢?

对不起,如果它看起来与其他问题类似,那可能是eben答案的原因,我想。 我所测量的是运行大量线程并知道所有线程何时完成,而不是轮询它们。

我更多地考虑使用批量线程在多个CPU之间共享CPU负载,并知道批处理何时完成。 我想它可以用Future的对象完成,但是阻塞get方法看起来很像隐藏锁,而不是我喜欢的东西。

谢谢大家的支持。 虽然我也很喜欢erickson的答案,我认为saua是最完整的,我将在我自己的代码中使用它。

除非您绝对需要强大的function和灵活性,否则不要使用线程等低级构造。

您可以使用ExecutorService (如ThreadPoolExecutor)来提交() Callables 。 这将返回一个Future对象。

使用Future对象,您可以轻松检查它是否已完成并获得结果(如果尚未完成,则包括阻塞get() )。

这些结构将大大简化最常见的线程操作。

我想澄清一下阻塞get()

这个想法是你想要运行一些任务( Callable s)来完成一些工作(计算,资源访问……)你现在不需要结果。 您可以随时依赖Executor来运行您的代码(如果它是ThreadPoolExecutor那么只要有空闲的Thread可用,它就会运行)。 然后在某个时间点,您可能需要继续计算结果。 此时你应该调用get() 。 如果任务已在该点运行,则get()将立即返回该值。 如果任务未完成,则get()调用将等待任务完成。 这通常是需要的,因为无论如何都不能在没有任务结果的情况下继续。

如果你不需要值继续,但想知道它是否已经可用(可能在UI中显示),那么你可以轻松调用isDone()并只调用get()如果返回true )。

你可以创建一个主程序实现的lister接口,一旦它完成了它的工作,就会被worker调用。

这样你根本不需要轮询。

这是一个示例界面:

 /** * Listener interface to implement to be called when work has * finished. */ public interface WorkerListener { public void workDone(WorkerThread thread); } 

下面是一个实际线程的示例,它执行一些工作并通知它的侦听器:

 import java.util.ArrayList; import java.util.Iterator; import java.util.List; /** * Thread to perform work */ public class WorkerThread implements Runnable { private List listeners = new ArrayList(); private List results; public void run() { // Do some long running work here try { // Sleep to simulate long running task Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } results = new ArrayList(); results.add("Result 1"); // Work done, notify listeners notifyListeners(); } private void notifyListeners() { for (Iterator iter = listeners.iterator(); iter.hasNext();) { WorkerListener listener = (WorkerListener) iter.next(); listener.workDone(this); } } public void registerWorkerListener(WorkerListener listener) { listeners.add(listener); } public List getResults() { return results; } } 

最后,启动工作线程并注册一个监听器的主程序在工作完成后得到通知:

 import java.util.Iterator; import java.util.List; /** * Class to simulate a main program */ public class MainProg { public MainProg() { WorkerThread worker = new WorkerThread(); // Register anonymous listener class worker.registerWorkerListener(new WorkerListener() { public void workDone(WorkerThread thread) { System.out.println("Work done"); List results = thread.getResults(); for (Iterator iter = results.iterator(); iter.hasNext();) { String result = (String) iter.next(); System.out.println(result); } } }); // Start the worker thread Thread thread = new Thread(worker); thread.start(); System.out.println("Main program started"); } public static void main(String[] args) { MainProg prog = new MainProg(); } } 

轮询也称为忙碌等待不是一个好主意。 正如您所提到的,繁忙等待会浪费CPU周期,并可能导致应用程序无响应。

我的Java很粗糙,但你需要类似以下内容:

如果一个线程必须等待另一个线程的输出,则应该使用条件变量。

 final Lock lock = new ReentrantLock(); final Condition cv = lock.newCondition(); 

对另一个威胁的输出感兴趣的线程应该调用cv.wait() 。 这将导致当前线程阻塞。 当工作线程完成工作时 ,它应该调用cv.signal() 。 这将导致被阻塞的线程被解除阻塞,允许它检查工作线程的输出。

作为Saua描述的并发API的替代方法(如果主线程不需要知道工作线程何时完成),您可以使用发布/订阅模式。

在这种情况下,子Thread / Runnable被赋予一个监听器 ,该监听器知道如何处理结果,并在子Thread / Runnable完成时回调。

您的方案仍然有点不清楚。

如果您正在运行批处理作业,则可能需要使用invokeAll 。 这将阻止您的主线程,直到所有任务完成。 这种方法没有“忙等待”,主线程会浪费CPU轮询Future的isDone方法。 虽然此方法返回Futures列表,但它们已经“完成”。 (还有一个重载版本可以在完成之前超时,这对于某些任务可能更安全。)这可能比尝试自己收集一堆Future对象并尝试检查其状态或阻止他们更清洁单独get方法。

如果这是一个交互式应用程序,偶尔分离出任务以在后台执行,那么使用nick.holt建议的回调是一种很好的方法。 在这里,您使用submit Runnablerun方法在计算结果时调用结果。 使用此方法,您可以丢弃由submit返回的Future ,除非您希望能够在不关闭整个ExecutorService情况下cancel正在运行的任务。

如果您希望能够取消任务或使用超时function,请记住一件重要的事情是通过在其线程上调用interrupt来取消任务。 因此,您的任务需要定期检查其中断状态,并根据需要中止。

子类Thread,并为您的类提供一个返回结果的方法。 调用该方法时,如果尚未创建结果,则使用Thread连接()。 当join()返回时,Thread的工作将完成,结果应该可用; 把它返还。

仅当您确实需要触发异步活动,在等待时执行某些操作然后获取结果时才使用此选项。 否则,线程的重点是什么? 您也可以编写一个完成工作的类,并在主线程中返回结果。

另一种方法是回调:让你的构造函数接受一个参数来实现一个带有回调方法的接口,该方法将在计算结果时被调用。 这将使工作完全异步。 但是如果你需要在某个时候等待结果,我认为你仍然需要从主线程中调用join()。

如saua所述:使用java.util.concurrent提供的构造。 如果你坚持使用1.5之前的(或5.0)JRE,那么你可能会采用自己的方式进行滚动,但是你仍然可以通过使用backport来提高效率: http : //backport-jsr166.sourceforge.net/