Saltar al contenido

¿Por qué funciona este CompletableFuture incluso cuando no llamo a get () o join ()?

Solución:

No se porque el Runnable el bloque de case2 está funcionando.

No hay ninguna razón por la que NO funcione.

los runAsync(...) El método dice hacer una tarea de forma asincrónica. Suponiendo que la aplicación no finalice prematuramente, la tarea se terminará eventualmente, ya sea que esperes a que se haga o no.

los CompletableFuture proporciona varias formas de esperar a que se complete la tarea. Pero en su ejemplo, no lo está usando para ese propósito. En cambio, el Thread.sleep(...) llamar a su método principal es teniendo el mismo efecto; es decir, está esperando el tiempo suficiente para que la tarea haya finalizado (probablemente). Entonces "Hello" se emite antes "World".

Solo para reiterar, el get() la llamada no porque la tarea a suceder. Más bien murga para que ha pasado.


Utilizando sleep esperar a que suceda un evento (por ejemplo, la finalización de una tarea) es una mala idea:

  1. ¡El sueño no dice si el evento ha sucedido!
  2. Por lo general, no sabe exactamente cuánto tiempo tardará en ocurrir el evento, no sabe cuánto tiempo dormir.
  3. Si duermes demasiado, tienes “tiempo muerto” (ver más abajo).
  4. Si no duerme lo suficiente, es posible que el evento aún no haya sucedido. Entonces necesitas probar y dormir una y otra vez, y …

Incluso en este ejemplo, es teóricamente posible1 Para el sleep en principal para terminar antes de la sleep en la tarea.

Básicamente, el propósito del CompletableFuture es proporcionar una forma eficaz de esperar a que finalice una tarea y obtener un resultado. Deberías usarlo …

Para ilustrar. Su aplicación está esperando (y desperdiciando) ~ 4 segundos entre la salida "Hello" y "World!". Si usaste el CompletableFuture como está destinado a ser utilizado, no tendrías esos 4 segundos de “tiempo muerto”.


1 – Por ejemplo, algún agente externo podría “pausar” selectivamente el hilo que está ejecutando la tarea. Podría hacerse estableciendo un punto de interrupción …

Toda la idea de CompletableFuture es que están programados para iniciarse de inmediato (aunque no puede decir de manera confiable en qué hilo se ejecutarán), y para cuando llegue get o join, es posible que el resultado ya esté listo, es decir: el CompletableFuture podría ser ya terminado. Internamente, tan pronto como una determinada etapa de la tubería esté lista, ese CompletableFuture se establecerá como completado. Por ejemplo:

String result = 
   CompletableFuture.supplyAsync(() -> "ab")
                    .thenApply(String::toUpperCase)
                    .thenApply(x -> x.substring(1))
                    .join();

es lo mismo que:

CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> "ab");
CompletableFuture<String> cf2 = cf1.thenApply(String::toUpperCase);
CompletableFuture<String> cf3 = cf2.thenApply(x -> x.substring(1));
String result = cf3.join();

Para cuando llegue a invocar realmente join, cf3 ya podría terminar. get y join solo cuadra hasta que se completen todas las etapas, no activa el cálculo; el cálculo se programa inmediatamente.


Una adición menor es que puede completar un CompletableFuture sin esperar a que termine la ejecución de los oleoductos: como complete, completeExceptionally, obtrudeValue (este lo configura incluso si ya se completó), obtrudeException o cancel. Aquí hay un ejemplo interesante:

 public static void main(String[] args) {
    CompletableFuture<String> cf = CompletableFuture.supplyAsync(() -> {
        System.out.println("started work");
        LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(5));
        System.out.println("done work");
        return "a";
    });

    LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
    cf.complete("b");
    System.out.println(cf.join());
}

Esto dará como resultado:

started work
b

Entonces, incluso si el trabajo comenzó, el valor final es b, no a.

El segundo caso es “laboral“porque duermes el hilo principal lo suficiente (5 segundos). El trabajo se realiza entre comillas porque realmente no está funcionando, acaba de terminar. Supongo que aquí el código debería generar Hello World! para ser considerado “trabajando apropiadamente“.


Pruebe el mismo código con este tiempo de reposo al final del hilo principal en ambos casos:

Thread.sleep(100);

1. El primero se comportaría de la misma manera, ya que la operación get bloquea el hilo principal. De hecho, para el primer caso, ni siquiera necesita la última hora de sueño.

Producción: Hello World!


2. El segundo caso no saldrá Hello, como nadie le dijo al hilo principal: “oye, espera a que termine esto“. Eso es lo que get() hace: bloquea a la persona que llama para esperar a que finalice la tarea. Sin él, y estableciendo un tiempo de reposo bajo al final, se llama al ejecutable, pero no pudo terminar su trabajo antes de que se detenga el subproceso principal.

Producción: World!


Esa es también la razón por la que en el primer caso Hello World! (primero la salida del ejecutable, y luego la principal, lo que significa que el hilo principal se bloqueó hasta get() regresó) está escrito, mientras que el segundo muestra signos sutiles de dislexia: World Hello!

Pero no es disléxico, simplemente ejecuta lo que se le dice. En el segundo caso, sucede esto:

1. El ejecutable es llamado.

2. El hilo principal continúa su proceso, imprimiendo (“¡Mundo!)

3. Sleep los tiempos están configurados: 1 segundo en el ejecutable / 5 segundos en el principal. (el sueño de runnable también podría ejecutarse durante el segundo paso, pero lo puse aquí para aclarar el comportamiento)

4. La tarea ejecutable impresiones (“Hola”) después de 1 segundo y el CompletableFuture está terminado.

5. Pasaron 5 segundos, el hilo principal se detiene.

Entonces tu ejecutable podría imprimir Hello porque pudo ejecutar el comando entre esos 5 segundos de tiempo de espera.

World! . . . . . .(1)Hello. . . . . . . . . . .(5)[END]

Si reduce el tiempo de espera de los últimos 5 segundos, por ejemplo, a 0,5 segundos, obtendrá

World!. . (0.5)[END]
¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *