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

发布于 2024-10-16 22:41:20 字数 218 浏览 4 评论 0原文

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

Since use ExecutorService can submit a Callable task and return a Future, why need to use FutureTask to wrap Callable task and use the method execute? I feel they both do the same thing.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

最佳男配角 2024-10-23 22:41:20

FutureTask
此类提供了Future的基本实现,以及启动和取消计算的方法

Future 是接口

FutureTask
This class provides a base implementation of Future, with methods to start and cancel a computation

Future is the interface

秋意浓 2024-10-23 22:41:20

事实上你是对的。这两种方法是相同的。您通常不需要自己包装它们。如果是,您可能会重复 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 实例存在多个引用。也就是说,执行者拥有这个实例。

In fact you are correct. The two approaches are identical. You generally don't need to wrap them yourself. If you are, you're likely duplicating the code in 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);
}

The only difference between Future and RunnableFuture, is the run() method:

/**
 * 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();
}

A good reason to let the Executor construct the FutureTask for you is to ensure that there is no possible way more than one reference exists to the FutureTask instance. That is, the Executor owns this instance.

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

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

Future is just interface. Behind the scene, the implementation is FutureTask.

You can absolutely use FutureTask manually but you will lose the advantages of using Executor (pooling thread, limit the thread, etc). Using FutureTask is quite similar to using the old Thread and using the run method.

只是在用心讲痛 2024-10-23 22:41:20

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

You would only need to use FutureTask if you want to change its behaviour or access its Callable later. For 99% of uses, just use Callable and Future.

命比纸薄 2024-10-23 22:41:20

正如 Mark 和其他人正确回答的那样,Future 是 FutureTask 的接口,而 Executor 实际上是它的工厂;这意味着应用程序代码很少直接实例化 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 缓存示例。

As Mark, and others, correctly answered that Future is the interface for FutureTask and Executor effectively its factory; meaning that application code rarely instantiates FutureTask directly. To complement the discussion I am providing an example showing a situation where FutureTask is constructed and used directly, outside any 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

Here, FutureTask is used as a synchronization tool, like CountdownLatch or similar barrier primitive. It could have been re-implemented by using CountdownLatch or locks and conditions; FutureTask just makes it nicely encapsulated, self-explanatory, elegant and with less code.

Also note that FutureTask#run() method must be called explicitly, in any of the threads; there no Executor around to do it for you. In my code, it is eventually executed by the main thread, but one can modify get() method to call run() on the first thread calling get(), therefore the first thread reaching get(), and it can be any of T1, T2 or T3, would do the calculation for all remaining threads.

On this idea - first thread requesting result would do the calculation for others, while concurrent attempts would be blocked - is based Memoizer, see Memoizer Cache example from page 108 in "Java Concurrency in Practice".

花落人断肠 2024-10-23 22:41:20

正如已经提到的,但不是笼统地说,而是更多技术术语,因为 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() 一样。

As already mentioned , but speaking not in general but in more of technical terms, since FutureTask implements RunnableFuture,
you can call it using

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

This is more inline with older runnable but it also has the capacity to return result via callback.

The greater power of FutureTask over Future lies in the fact that it has more control over threads as against just simply Submitting a callable to Future and let executor handle the threads.

like you can call here t1.join().

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文