这是使用java.util.concurrent.FutureTask的好方法吗?
首先,我必须说我对API java.util.concurrent很陌生,所以也许我所做的是完全错误的。
我想做什么?
我有一个基本上运行2个独立处理(称为myFirstProcess , mySecondProcess )的Java应用程序,但是这些处理必须同时运行。
所以,我试图这样做:
public void startMyApplication() { ExecutorService executor = Executors.newFixedThreadPool(2); FutureTask<Object> futureOne = new FutureTask<Object>(myFirstProcess); FutureTask<Object> futureTwo = new FutureTask<Object>(mySecondProcess); executor.execute(futureOne); executor.execute(futureTwo); while (!(futureOne.isDone() && futureTwo.isDone())) { try { // I wait until both processes are finished. Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } logger.info("Processing finished"); executor.shutdown(); // Do some processing on results ... }
myFirstProcess和mySecondProcess是实现Callable<Object>
,并且在call()方法中完成所有处理。
它工作得很好,但我不确定这是否正确。 做我想要的是一个好方法吗? 如果没有,你可以给我一些提示,以提高我的代码(并保持尽可能简单)。
你最好使用get()
方法。
futureOne.get(); futureTwo.get();
两者都等待线程完成处理的通知,这样可以节省您正在使用的busy-wait-with-timer,效率和优雅性都不高。
作为奖励,你有API get(long timeout, TimeUnit unit)
,它允许你定义线程hibernate的最大时间并等待响应,否则继续运行。
有关更多信息,请参阅Java API 。
以上FutureTask
的使用是可以忍受的,但绝对不是惯用的。 实际上,您将一个额外的 FutureTask
包装在您提交给ExecutorService
那个FutureTask
附近。 您的FutureTask
被ExecutorService
视为Runnable
。 在内部,它将FutureTask
-as- Runnable
包装在一个新的FutureTask
,并将其作为Future<?>
返回给您。
相反,您应该将您的Callable<Object>
实例提交给CompletionService
。 您可以通过submit(Callable<V>)
放弃两个Callable
s,然后转身并调用CompletionService#take()
两次(每个提交的Callable
一次)。 这些呼叫将阻塞,直到一个,然后提交的其他任务完成。
考虑到你已经有一个Executor
了,围绕它构造一个新的ExecutorCompletionService
,并把你的任务放在那里。 不要旋转和睡觉等待; CompletionService#take()
将会阻塞,直到任一个任务完成(或者完成运行或取消)或者等待take()
的线程中断。
Yuval的解决scheme是好的。 作为一种select,你也可以这样做:
ExecutorService executor = Executors.newFixedThreadPool(); FutureTask<Object> futureOne = new FutureTask<Object>(myFirstProcess); FutureTask<Object> futureTwo = new FutureTask<Object>(mySecondProcess); executor.execute(futureOne); executor.execute(futureTwo); executor.shutdown(); try { executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); } catch (InterruptedException e) { // interrupted }
这种方法的优点是什么? 除了这种方式,你可以阻止执行者接受更多的任务(你也可以这样做),除此之外没有什么区别。 我倾向于喜欢这个成语。
另外,如果get()抛出一个exception,你最终可能会在你的代码的一部分,假定这两个任务完成,这可能是不好的。
你可以使用invokeall(Colelction ….)方法
package concurrent.threadPool; import java.util.Arrays; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class InvokeAll { public static void main(String[] args) throws Exception { ExecutorService service = Executors.newFixedThreadPool(5); List<Future<java.lang.String>> futureList = service.invokeAll(Arrays.asList(new Task1<String>(),new Task2<String>())); System.out.println(futureList.get(1).get()); System.out.println(futureList.get(0).get()); } private static class Task1<String> implements Callable<String>{ @Override public String call() throws Exception { Thread.sleep(1000 * 10); return (String) "1000 * 5"; } } private static class Task2<String> implements Callable<String>{ @Override public String call() throws Exception { Thread.sleep(1000 * 2); int i=3; if(i==3) throw new RuntimeException("Its Wrong"); return (String) "1000 * 2"; } } }
如果您有兴趣同时启动线程,或等待它们完成,然后再做一些处理,则可能需要使用CyclicBarrier 。 有关更多信息,请参阅javadoc。
如果您的未来任务超过2,请考虑[ListenableFuture][1]
。
当另一个操作开始时,几个操作应该开始 – “ 扇出 ” – ListenableFuture只是起作用:触发所有请求的callback。 只要稍微做些工作,我们就可以“煽风点火”,或者在其他几个期货全部完成的情况下立刻触发ListenableFuture。