web-dev-qa-db-ja.com

JavaのFutureとFutureTaskの違いは何ですか?

ExecutorServiceを使用するとsubmitCallableタスクを返し、Futureを返すことができるので、なぜFutureTaskを使用してCallableタスクをラップし、メソッドexecuteを使用する必要があるのですか?どちらも同じことをしていると思う。

48
Hesey

実際、あなたは正しいです。 2つのアプローチは同じです。通常、自分でラップする必要はありません。もしそうなら、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インスタンスに複数の参照が存在する可能性のある方法がないことを保証するためです。つまり、エグゼキューターownsこのインスタンス。

26
Mark Renouf

FutureTask このクラスはbase implementation of Future、計算を開始およびキャンセルするメソッド

将来 はインターフェースです

41
fmucar

Futureは単なるインターフェイスです。舞台裏では、実装はFutureTaskです。

絶対に手動でFutureTaskを使用できますが、Executor(プールスレッド、スレッドの制限など)を使用する利点は失われます。 FutureTaskの使用は、古いThreadの使用とrunメソッドの使用に非常に似ています。

16
nanda

FutureTaskを使用する必要があるのは、その動作を変更するか、後でCallableにアクセスする場合のみです。 99%の用途では、CallableとFutureを使用してください。

7
Peter Lawrey

Markや他の人たちは、FutureFutureTaskExecutorのインターフェースであり、事実上そのファクトリーであると正しく答えました。つまり、アプリケーションコードが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の例を参照してください。

3
Espinosa