web-dev-qa-db-fra.com

Quelle est la différence entre Future et FutureTask en Java?

Puisque use ExecutorService peut submit une tâche Callable et retourne Future, pourquoi faut-il utiliser FutureTask pour envelopper Callable tâche et utiliser la méthode execute? Je sens qu'ils font tous les deux la même chose.

48
Hesey

En fait, vous avez raison. Les deux approches sont identiques. Vous n'avez généralement pas besoin de les emballer vous-même. Si vous l'êtes, vous dupliquez probablement le code dans 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);
}

La seule différence entre Future et RunnableFuture, est la méthode 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();
}

Une bonne raison de laisser l'exécuteur construire la FutureTask pour vous est de vous assurer qu'il n'existe aucun moyen possible qu'il existe plusieurs références à l'instance de FutureTask. Autrement dit, l'exécuteur possède cette instance.

26
Mark Renouf

FutureTask Cette classe fournit un base implementation of Future, avec des méthodes pour démarrer et annuler un calcul

Future est l'interface

41
fmucar

Future n'est qu'une interface. En coulisse, l'implémentation est FutureTask.

Vous pouvez absolument utiliser FutureTask manuellement mais vous perdrez les avantages d'utiliser Executor (regroupement de threads, limitation du thread, etc.). L'utilisation de FutureTask est assez similaire à l'utilisation de l'ancienne Thread et à l'utilisation de la méthode run.

16
nanda

Vous n'auriez besoin d'utiliser FutureTask que si vous souhaitez modifier son comportement ou accéder à son Callable ultérieurement. Pour 99% des utilisations, utilisez simplement Callable et Future.

7
Peter Lawrey

Comme Mark et d'autres ont répondu correctement que Future est l'interface pour FutureTask et Executor efficacement son usine; ce qui signifie que le code d'application instancie rarement FutureTask directement. Pour compléter la discussion, je fournis un exemple montrant une situation où FutureTask est construit et utilisé directement, en dehors de tout 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

Ici, FutureTask est utilisé comme outil de synchronisation, comme CountdownLatch ou une primitive barrière similaire. Il aurait pu être réimplémenté en utilisant CountdownLatch ou des verrous et des conditions; FutureTask le rend simplement bien encapsulé, explicite, élégant et avec moins de code.

Notez également que la méthode FutureTask # run () doit être appelée explicitement, dans l'un des threads; il n'y a pas d'exécuteur pour le faire pour vous. Dans mon code, il est éventuellement exécuté par le thread principal, mais on peut modifier la méthode get() pour appeler run() sur le premier thread appelant get(), donc le premier thread atteignant get(), et il peut s'agir de T1, T2 ou T3, ferait le calcul pour tous les threads restants.

Sur cette idée - le premier thread demandant le résultat ferait le calcul pour les autres, tandis que les tentatives simultanées seraient bloquées - est basé Memoizer, voir l'exemple Memoizer Cache de la page 108 dans "Java Concurrency in Practice".

3
Espinosa