由于使用ExecutorService
可以submit
Callable
任务并返回Future
,为什么需要使用FutureTask
来包装Callable
任务,使用方法execute
?我觉得他们都做同样的事情。
答案 0 :(得分:41)
FutureTask
该类提供base implementation of Future
,其中包含启动和取消计算的方法
Future是界面
答案 1 :(得分:25)
事实上你是对的。这两种方法是相同的。您通常不需要自己包装它们。如果是,您可能会复制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为您构造FutureTask的一个很好的理由是确保FutureTask实例不存在多个引用的可能方式。也就是说,执行者拥有这个实例。
答案 2 :(得分:15)
Future
只是界面。在幕后,实施是FutureTask
。
您绝对可以手动使用FutureTask
但是您将失去使用Executor
(池化线程,限制线程等)的优势。使用FutureTask
与使用旧Thread
并使用运行方法非常相似。
答案 3 :(得分:6)
如果您想要更改其行为或稍后访问其Callable,您只需要使用FutureTask。对于99%的用途,只需使用Callable和Future。
答案 4 :(得分:2)
正如马克和其他人一样,正确回答Future
是FutureTask
和Executor
有效工厂的界面;意味着应用程序代码很少直接实例化FutureTask
。为了补充讨论,我提供了一个示例,显示FutureTask
在任何Executor
之外构建和直接使用的情况:
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
只是简单地封装,不言自明,优雅且代码更少。
另请注意,必须在任何线程中显式调用FutureTask#run()方法;没有执行官为你做这件事。在我的代码中,它最终由主线程执行,但是可以修改get()
方法以在调用run()
的第一个线程上调用get()
,因此第一个线程到达get()
它可以是T1,T2或T3中的任何一个,可以对所有剩余的线程进行计算。
关于这个想法 - 第一个线程请求结果将为其他人进行计算,而并发尝试将被阻止 - 基于Memoizer,请参阅“实践中Java并发”中的第108页的Memoizer Cache示例。
答案 5 :(得分:0)
正如已经提到的,但不是笼统地说,而是更多的技术术语,因为 FutureTask 实现了 RunnableFuture, 您可以使用
调用它FutureTask<T> result = new FutureTask<T>(new #YourClassImplementingCallable());
Thread t1= new Thread(result);
t1.start();
Object<T> obj = result.get();
这更符合旧的 runnable,但它也有通过回调返回结果的能力。
FutureTask 比 Future 更强大的功能在于它对线程有更多的控制,而不是简单地向 Future 提交一个可调用对象并让执行程序处理线程。
就像你可以在这里调用 t1.join()。