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和其他人所正确回答, Future
是FutureTask
和Executor
有效工厂的接口; 这意味着应用程序代码很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示例。