java - statement - Llamada de método a los bloques Future.get(). ¿Es eso realmente deseable?
try catch finally java ejemplos (5)
A continuación se muestra el fragmento del pseudo código. Mi pregunta es: ¿el código a continuación no derrota la noción misma de procesamiento asincrónico paralelo?
Todo depende de tu caso de uso:
- Si realmente quieres bloquear hasta obtener el resultado, usa el bloqueo
get()
- Si puede esperar un período específico para conocer el estado en lugar de una duración de bloqueo infinita, use
get()
con tiempo de espera Si puede continuar sin analizar el resultado de inmediato e inspeccionar el resultado en el futuro, utilice CompletableFuture (java 8)
Un Futuro que puede completarse explícitamente (establecer su valor y estado), y puede usarse como un CompletionStage, que admite funciones dependientes y acciones que se desencadenan al completarse.
Puede implementar el mecanismo de devolución de llamada desde su Runnable / Callable. Echa un vistazo a la siguiente pregunta SE:
Ejecutores de Java: cómo ser notificado, sin bloqueo, cuando una tarea se completa?
Lea la pregunta cuidadosamente antes de marcar esto como duplicado.
A continuación se muestra el fragmento del pseudo código. Mi pregunta es: ¿el código a continuación no derrota la noción misma de procesamiento asincrónico paralelo?
La razón por la que pregunto esto es porque en el siguiente código el hilo principal enviaría una tarea para ser ejecutada en un hilo diferente. Después de enviar la tarea en la cola, bloquea el método Future.get () para que la tarea devuelva el valor. Prefiero tener la tarea ejecutada en el hilo principal en lugar de enviarla a un hilo diferente y esperar los resultados. ¿Qué es eso que gané al ejecutar la tarea en un nuevo hilo?
Soy consciente de que puedes esperar durante un tiempo limitado, etc., pero ¿qué ocurre si realmente me importa el resultado? El problema empeora si hay varias tareas para ejecutar. Me parece que solo estamos haciendo el trabajo sincrónicamente. Conozco la biblioteca de Guava que proporciona una interfaz de escucha sin bloqueo. Pero estoy interesado en saber si mi comprensión es correcta para la API Future.get (). Si es correcto, ¿por qué está diseñado el Future.get () para bloquear, derrotando así todo el proceso de procesamiento paralelo?
Nota - Para el registro, uso JAVA 6
public static void main(String[] args){
private ExectorService executorService = ...
Future future = executorService.submit(new Callable(){
public Object call() throws Exception {
System.out.println("Asynchronous Callable");
return "Callable Result";
}
});
System.out.println("future.get() = " + future.get());
}
En el ejemplo que le ha dado, también podría ejecutar todo en su método main()
y seguir su camino feliz.
Pero supongamos que tiene tres pasos de cálculo que actualmente está ejecutando de forma secuencial. Solo para entenderlo, supongamos que el paso 1 lleva t1 segundos, el segundo paso lleva t2 segundos y el paso 3 tarda t3 segundos en completarse. Entonces, el tiempo total de cálculo es t1+t2+t3
. Además, supongamos que t2>t1>=t3
.
Ahora consideremos un escenario cuando ejecutamos estos tres pasos en paralelo usando Future
para mantener cada resultado computacional. Puede verificar si cada tarea se realiza utilizando la llamada non-blocking isDone()
en los futuros correspondientes. Ahora que pasa? teóricamente su ejecución es tan rápida como la t2
completa ¿verdad? Así que obtuvimos algunos beneficios del paralelismo.
Además, en Java8, hay CompletableFuture
que admite llamadas de estilo funcional.
Me gustaría dar mi parte en esto, más en el punto de vista teórico ya que hay algunas respuestas técnicas. Me gustaría basar mi respuesta en el comentario:
Déjame darte mi ejemplo. Las tareas que envío al servicio terminan generando solicitudes HTTP. El resultado de la solicitud HTTP puede llevar mucho tiempo. Pero sí necesito el resultado de cada solicitud HTTP. Las tareas se envían en un bucle. Si espero que cada tarea regrese (entregue), entonces estoy perdiendo el paralelismo aquí, ¿no?
que está de acuerdo con lo que se dice en la pregunta.
Digamos que tienes tres hijos y quieres hacer un pastel, por tu cumpleaños. Como quieres hacer los mejores pasteles, necesitas muchas cosas diferentes para prepararlos. Entonces, lo que haces es dividir los ingredientes en tres listas diferentes, porque donde vives solo existen 3 supermercados que venden diferentes productos y asignan a cada uno de tus hijos una única tarea, simoultaneosly
.
Ahora, antes de que pueda comenzar a preparar el pastel (supongamos nuevamente, que necesita todos los ingredientes de antemano) tendrá que esperar al niño que tiene que hacer la ruta más larga. Ahora, el hecho de que necesite esperar por todos los ingredientes antes de comenzar a preparar el pastel es su necesidad, no una dependencia entre tareas. Sus hijos han estado trabajando en las tareas de manera simultánea todo el tiempo que pudieron (por ejemplo, hasta que el primer niño completó la tarea). Entonces, para concluir, aquí tienes el paralelilsm.
El ejemplo secuencial se describe cuando tienes 1 niño y le asignas las tres tareas.
Si no le importan los resultados, cree un nuevo hilo y use el API ExectorService
para el envío de tareas. De esta forma, el hilo padre, es decir, el hilo main
, no bloqueará de ninguna manera, simplemente generará un nuevo hilo y luego comenzará una ejecución posterior, mientras que el nuevo hilo enviará sus tareas.
Para crear un nuevo hilo, hágalo usted mismo teniendo un ThreadFactory
para su creación de hilo asincrónico o utilice alguna implementación de java.util.concurrent.Executor
.
Si esto está en una aplicación JEE y está utilizando el marco Spring, entonces puede crear fácilmente un nuevo hilo asincrónico utilizando @async
annotation.
¡Espero que esto ayude!
Future
ofrece el método isDone()
que no está bloqueando y devuelve verdadero si el cálculo se ha completado, en caso contrario, es falso.
Future.get()
se usa para recuperar el resultado del cálculo.
Tienes unas cuantas opciones:
- llame a
isDone()
y si el resultado está listo, solicítelo invocandoget()
, observe cómo no hay bloqueo - bloquear indefinidamente con
get()
- bloquear el tiempo de espera especificado con
get(long timeout, TimeUnit unit)
Todo el Future API
está ahí para tener una forma sencilla de obtener valores de hilos ejecutando tareas paralelas. Esto se puede hacer de forma síncrona o asíncrona, si lo prefiere, como se describe en las viñetas anteriores.
ACTUALIZACIÓN CON CACHE EJEMPLO
Aquí hay una implementación de caché de Java Concurrency In Practice , un excelente caso de uso para Future
.
- Si el cálculo ya se está ejecutando, el llamador interesado en el resultado de la computación esperará a que finalice el cálculo
- Si el resultado está listo en la memoria caché, la persona que llama lo recogerá
- si el resultado no está listo y la computación aún no ha comenzado, la persona que llama iniciará el cálculo y ajustará el resultado en
Future
para otras personas que llaman.
Todo esto se logra fácilmente con Future
API.
package net.jcip.examples;
import java.util.concurrent.*;
/**
* Memoizer
* <p/>
* Final implementation of Memoizer
*
* @author Brian Goetz and Tim Peierls
*/
public class Memoizer <A, V> implements Computable<A, V> {
private final ConcurrentMap<A, Future<V>> cache
= new ConcurrentHashMap<A, Future<V>>();
private final Computable<A, V> c;
public Memoizer(Computable<A, V> c) {
this.c = c;
}
public V compute(final A arg) throws InterruptedException {
while (true) {
Future<V> f = cache.get(arg);
// computation not started
if (f == null) {
Callable<V> eval = new Callable<V>() {
public V call() throws InterruptedException {
return c.compute(arg);
}
};
FutureTask<V> ft = new FutureTask<V>(eval);
f = cache.putIfAbsent(arg, ft);
// start computation if it''s not started in the meantime
if (f == null) {
f = ft;
ft.run();
}
}
// get result if ready, otherwise block and wait
try {
return f.get();
} catch (CancellationException e) {
cache.remove(arg, f);
} catch (ExecutionException e) {
throw LaunderThrowable.launderThrowable(e.getCause());
}
}
}
}