Java中的Future和FutureTask有啥区别?

Posted

技术标签:

【中文标题】Java中的Future和FutureTask有啥区别?【英文标题】:What's the difference between Future and FutureTask in Java?Java中的Future和FutureTask有什么区别? 【发布时间】:2011-06-24 19:36:40 【问题描述】:

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

【问题讨论】:

【参考方案1】:

Future 只是接口。在幕后,实现是FutureTask

您绝对可以手动使用FutureTask,但您将失去使用Executor 的优势(池化线程、限制线程等)。使用 FutureTask 与使用旧的 Thread 和使用 run 方法非常相似。

【讨论】:

FutureTask同时实现了Future和Runnable,为什么不能提交给ExecutorService呢? 我觉得FutureTask可以提交给Executor? “一个FutureTask可以用来包装一个Callable或Runnable对象。因为FutureTask实现了Runnable,所以一个FutureTask可以提交给一个Executor执行。” docs.oracle.com/javase/7/docs/api/java/util/concurrent/…【参考方案2】:

如果你想改变它的行为或稍后访问它的 Callable,你只需要使用 FutureTask。对于 99% 的使用,只需使用 Callable 和 Future。

【讨论】:

【参考方案3】:

其实你是对的。这两种方法是相同的。您通常不需要自己包装它们。如果是这样,您很可能复制了 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 实例的引用不会超过一个。也就是说,Executor拥有这个实例。

【讨论】:

FutureTask.get() 从不抛出 CancellationException,而 Future.get() 会。它是否正确?见docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/…,java.util.concurrent.TimeUnit)。【参考方案4】:

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

Future是接口

【讨论】:

【参考方案5】:

Mark 和其他人正确回答FutureFutureTaskExecutor 的接口,实际上是它的工厂;这意味着应用程序代码很少直接实例化FutureTask。为了补充讨论,我提供了一个示例,展示了在任何 Executor 之外直接构造和使用 FutureTask 的情况:

    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()方法在第一个调用get()的线程上调用run(),因此第一个线程到达get(),它可以是任何一个T1、T2 或 T3 将对所有剩余线程进行计算。

关于这个想法 - 第一个线程请求结果将为其他线程进行计算,而并发尝试将被阻止 - 基于 Memoizer,请参阅“Java 并发实践”中第 108 页的 Memoizer 缓存示例。

【讨论】:

【参考方案6】:

如前所述,但不是笼统地说,而是更多的技术术语,因为 FutureTask 实现了 RunnableFuture, 你可以调用它使用

FutureTask<T> result = new FutureTask<T>(new #YourClassImplementingCallable());
Thread t1= new Thread(result);
t1.start();
Object<T> obj = result.get();

这与旧的可运行文件更内联,但它也可以通过回调返回结果。

FutureTask 优于 Future 的强大之处在于它对线程有更多的控制,而不是简单地向 Future 提交一个可调用对象并让执行程序处理线程。

就像你可以在这里调用 t1.join()。

【讨论】:

以上是关于Java中的Future和FutureTask有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章

Java——聊聊JUC中的Future和FutureTask

Java——聊聊JUC中的Future和FutureTask

Java中的Future是如何实现的

Callable, Runnable, Future, FutureTask

FutureTask

java多线程Callable,Future,FutureTask