es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Cómo forzar que CompletableFuture.thenApply() se ejecute en el mismo hilo que ejecutó la etapa anterior?

Aquí está una versión de código corto del problema que estoy enfrentando:

public static void main(String[] args) {
    CompletableFuture.supplyAsync(() -> {
                /*
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException ignored) {}
                */
                //System.out.println("supplyAsync: " + Thread.currentThread().getName());
                return 1;
            })
            .thenApply(i -> {
                System.out.println("apply: " + Thread.currentThread().getName());
                return i + 1;
            })
            .thenAccept((i) -> {
                System.out.println("accept: " + Thread.currentThread().getName());
                System.out.println("result: " + i);
            }).join();
}

Este es el resultado que obtengo:

apply: main
accept: main
result: 2

¡Me sorprende ver “main” allí! Esperaba algo como esto que sucede cuando descomento la llamada Thread.sleep() o incluso descomento la única declaración sysout allí:

supplyAsync: ForkJoinPool.commonPool-worker-1
apply: ForkJoinPool.commonPool-worker-1
accept: ForkJoinPool.commonPool-worker-1
result: 2

Entiendo que thenApplyAsync() se asegurará de que no se ejecute en el hilo “main”, pero quiero evitar pasar los datos devueltos por el suministrador desde el hilo que ejecutó supplyAsync al hilo que ejecutará thenApply y los otros then subsiguientes en la cadena.

Tags:  , , , ,

Answer

  1. Avatar for davy.ai

    La razón por la que ves main en la salida es porque la llamada supplyAsync() utiliza el ForkJoinPool.commonPool() por defecto, que es un conjunto compartido de hilos utilizado por la JVM para ejecutar tareas. En este caso, como no hay otras tareas que se estén ejecutando actualmente, el hilo main se utiliza para ejecutar las llamadas thenApply() y thenAccept().

    Si quieres evitar pasar los datos devueltos por el proveedor desde el hilo que ejecutó supplyAsync() hacia el hilo que ejecutará thenApply() y los demás then subsiguientes en la cadena, puedes utilizar el método thenApplyAsync() en lugar de thenApply(). Esto asegurará que cada operación subsiguiente en la cadena se ejecute en un hilo separado al hilo que ejecutó la operación anterior, sin pasar ningún dato entre ellos.

    Aquí tienes una versión actualizada del código que utiliza thenApplyAsync():

    public static void main(String[] args) {
        CompletableFuture.supplyAsync(() -> {
                    /*
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException ignored) {}
                    */
                    //System.out.println("supplyAsync: " + Thread.currentThread().getName());
                    return 1;
                })
                .thenApplyAsync(i -> {
                    System.out.println("apply: " + Thread.currentThread().getName());
                    return i + 1;
                })
                .thenAcceptAsync((i) -> {
                    System.out.println("accept: " + Thread.currentThread().getName());
                    System.out.println("result: " + i);
                }).join();
    }
    

    Dado que estamos utilizando thenApplyAsync(), cada operación subsiguiente en la cadena se ejecutará en un hilo separado al hilo que ejecutó la operación anterior, sin pasar ningún dato entre ellos. Esto te dará la salida esperada:

    apply: ForkJoinPool.commonPool-worker-1
    accept: ForkJoinPool.commonPool-worker-1
    result: 2
    

    Ten en cuenta que los nombres exactos de los hilos pueden variar dependiendo del sistema y la configuración de la JVM.

Comments are closed.