2011-02-10 34 views

Respuesta

21

De hecho, tiene razón. Los dos enfoques son idénticos. Por lo general, no es necesario que los envuelva usted mismo. Si es así, es muy probable duplicando el código en 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 única diferencia entre el futuro y RunnableFuture, es el método 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(); 
} 

Una buena razón para dejar que el Ejecutor construir el FutureTask para usted es asegurarse de que no haya manera posible de que exista más de una referencia a la instancia FutureTask. Es decir, el Ejecutor posee esta instancia.

+0

FutureTask.get() nunca lanza una CancellationException, mientras que Future.get() sí lo hace. ¿Es esto correcto? Consulte http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/FutureTask.html#get(long, java.util.concurrent.TimeUnit). –

13

Future es solo una interfaz. Detrás de la escena, la implementación es FutureTask.

Puede usar completamente FutureTask manualmente, pero perderá las ventajas de usar Executor (agrupamiento de hilo, limitar el hilo, etc.). El uso de FutureTask es bastante similar al uso del antiguo Thread y el ejecuta el método.

+1

FutureTask implementa Future y Runnable, entonces ¿por qué no se puede enviar a ExecutorService? –

5

Solo necesitaría utilizar FutureTask si desea cambiar su comportamiento o acceder a su Callable más adelante. Para el 99% de los usos, solo use Callable y Future.

38

FutureTask Esta clase proporciona una base implementation of Future, con métodos para iniciar y cancelar un cálculo

Future es la interfaz

1

como Mark y otros, respondió correctamente que Future es la interfaz para FutureTask y Executor efectiva su fábrica; lo que significa que el código de la aplicación rara vez instancia FutureTask directamente. Para complementar la discusión que estoy proporcionando un ejemplo que muestra una situación en la FutureTask se construye y se usa directamente, fuera de cualquier 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 

Aquí, FutureTask se utiliza como una herramienta de sincronización, como CountdownLatch o barrera similar primitivo. Se podría haber reimplementado usando CountdownLatch o bloqueos y condiciones; FutureTask simplemente lo hace muy bien encapsulado, autoexplicativo, elegante y con menos código.

También tenga en cuenta que el método FutureTask # run() debe invocarse explícitamente en cualquiera de los hilos; no hay ningún ejecutor a tu alrededor que lo haga por ti. En mi código, es eventualmente ejecutado por el hilo principal, pero se puede modificar el método get() para llamar al run() en el primer hilo llamando al get(), por lo tanto, el primer hilo que alcance get(), y cualquiera de T1, T2 o T3, podría el cálculo para todos los hilos restantes.

En esta idea, el primer resultado de la solicitud de subprocesos haría el cálculo para otros, mientras que los intentos concurrentes serían bloqueados, es Memoizer basado, consulte Ejemplo de memoria caché de memoria de la página 108 en "Simultaneidad de Java en la práctica".

Cuestiones relacionadas