Future和FutureTask在Java中有什么区别?

由于使用ExecutorService可以submit一个Callable任务并返回一个Future ,为什么需要使用FutureTask来包装Callable任务并使用方法execute ? 我觉得他们都做同样的事情。

其实你是对的。 这两种方法是相同的。 你通常不需要自己包装。 如果你是,你很可能复制AbstractExecutorService中的代码:

 /** * Returns a <tt>RunnableFuture</tt> for the given callable task. * * @param callable the callable task being wrapped * @return a <tt>RunnableFuture</tt> which when run will call the * underlying callable and which, as a <tt>Future</tt>, will yield * the callable's result as its result and provide for * cancellation of the underlying task. * @since 1.6 */ protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) { return new FutureTask<T>(callable); } 

Future和RunnableFuture之间的唯一区别是run()方法:

 /** * A {@link Future} that is {@link Runnable}. Successful execution of * the <tt>run</tt> method causes completion of the <tt>Future</tt> * and allows access to its results. * @see FutureTask * @see Executor * @since 1.6 * @author Doug Lea * @param <V> The result type returned by this Future's <tt>get</tt> method */ public interface RunnableFuture<V> extends Runnable, Future<V> { /** * Sets this Future to the result of its computation * unless it has been cancelled. */ void run(); } 

让Executor为您构buildFutureTask的一个很好的理由是确保FutureTask实例不存在多于一个的引用。 也就是说,执行者拥有这个实例。

FutureTask这个类提供了base implementation of Future一个base implementation of Future ,包含启动和取消计算的方法

未来是界面

Future只是界面。 在幕后,执行是FutureTask

你可以绝对使用FutureTask ,但是你将失去使用Executor的好处(合并线程,限制线程等)。 使用FutureTask与使用旧的Thread和使用run方法非常相似。

如果您想更改其行为或稍后访问其Callable,则只需要使用FutureTask。 对于99%的使用,只需使用Callable和Future。

正如Mark和其他人所正确回答, FutureFutureTaskExecutor有效工厂的接口; 这意味着应用程序代码很less直接实例化FutureTask 。 为了补充讨论,我提供了一个例子,显示FutureTask在任何Executor之外直接构build和使用的情况:

  FutureTask<Integer> task = new FutureTask<Integer>(()-> { System.out.println("Pretend that something complicated is computed"); Thread.sleep(1000); return 42; }); Thread t1 = new Thread(()->{ try { int r = task.get(); System.out.println("Result is " + r); } catch (InterruptedException | ExecutionException e) {} }); Thread t2 = new Thread(()->{ try { int r = task.get(); System.out.println("Result is " + r); } catch (InterruptedException | ExecutionException e) {} }); Thread t3 = new Thread(()->{ try { int r = task.get(); System.out.println("Result is " + r); } catch (InterruptedException | ExecutionException e) {} }); System.out.println("Several threads are going to wait until computations is ready"); t1.start(); t2.start(); t3.start(); task.run(); // let the main thread to compute the value 

在这里, FutureTask被用作同步工具,如CountdownLatch或类似的障碍基元。 它可以通过使用CountdownLatch或锁和条件来重新实现; FutureTask只是很好的封装,不言自明,优雅,代码less。

另外请注意,FutureTask#run()方法必须在任何线程中显式调用; 那里没有Executor为你做。 在我的代码中,它最终由主线程执行,但是可以在调用get()的第一个线程上修改get()方法来调用run() get() ,因此第一个线程到达get() ,它可以是T1,T2或T3,将执行所有剩余线程的计算。

在这个想法上 – 第一个线程请求的结果会为其他人做计算,而并发的尝试将被阻止 – 是基于Memoizer的,参见“实践中的Java并发”中的第108页的Memoizer Cache示例。

Interesting Posts