Llamando ExecutorService.shutdownNow de CompletableFuture

tsolakp:

Necesito cancelar todo programado, pero aún no la ejecución de tareas CompletableFuture cuando uno de tarea ya se está ejecutando, se emite una excepción.

Intentado siguiente ejemplo, pero la mayoría de las veces el principal método no sale (probablemente debido a algún tipo de punto muerto).

public static void main(String[] args) {
    ExecutorService executionService = Executors.newFixedThreadPool(5);

    Set< CompletableFuture<?> > tasks = new HashSet<>();

    for (int i = 0; i < 1000; i++) {
        final int id = i;
        CompletableFuture<?> c = CompletableFuture

        .runAsync( () -> {
            System.out.println("Running: " + id); 
            if ( id == 400 ) throw new RuntimeException("Exception from: " + id);
        }, executionService )

        .whenComplete( (v, ex) -> { 
            if ( ex != null ) {
                System.out.println("Shutting down.");
                executionService.shutdownNow();
                System.out.println("shutdown.");
            }
        } );

        tasks.add(c);
    }

    try{ 
        CompletableFuture.allOf( tasks.stream().toArray(CompletableFuture[]::new) ).join(); 
    }catch(Exception e) { 
        System.out.println("Got async exception: " + e); 
    }finally { 
        System.out.println("DONE"); 
    }        
}

Última impresión es algo como esto:

Running: 402
Running: 400
Running: 408
Running: 407
Running: 406
Running: 405
Running: 411
Shutting down.
Running: 410
Running: 409
Running: 413
Running: 412
shutdown.

Intentado funcionar con shutdownNowel método de hilo separado pero aún así, la mayoría de las veces, da el mismo punto muerto.

¿Alguna idea de lo que podría causar este punto muerto?

Y lo que cree que es la mejor manera de cancelar la totalidad programados pero que aún no se ejecuta CompletableFutures cuando se lanza una excepción?

Estaba pensando en una iteración tasksy llamando cancela cada CompletableFuture. Pero lo que no me gusta de esto es que los tiros CancellationExceptionde join.

Holger:

Debe tener en cuenta que

CompletableFuture<?> f = CompletableFuture.runAsync(runnable, executionService);

es básicamente equivalente a

CompletableFuture<?> f = new CompletableFuture<>();
executionService.execute(() -> {
    if(!f.isDone()) {
        try {
            runnable.run();
            f.complete(null);
        }
        catch(Throwable t) {
            f.completeExceptionally(t);
        }
    }
});

Por lo que el ExecutorServiceno sabe nada sobre el CompletableFuture, por lo tanto, no puede cancelarla en general . Todo lo que tiene, es un poco de trabajo, expresado como una implementación de Runnable.

En otras palabras, shutdownNow()impida la ejecución de los trabajos pendientes, por lo tanto, los futuros restantes no conseguirá completado normalmente, pero no va a cancelarlos. A continuación, se llama join()en el futuro devuelto por allOfla cual nunca volver debido a los futuros nunca acabado.

Pero tenga en cuenta que la tarea programada no comprueba si el futuro ya está terminado antes de hacer nada caro.

Por lo tanto, si cambia su código de

ExecutorService executionService = Executors.newFixedThreadPool(5);
Set<CompletableFuture<?>> tasks = ConcurrentHashMap.newKeySet();
AtomicBoolean canceled = new AtomicBoolean();

for(int i = 0; i < 1000; i++) {
    final int id = i;
    CompletableFuture<?> c = CompletableFuture
        .runAsync(() -> {
            System.out.println("Running: " + id); 
            if(id == 400) throw new RuntimeException("Exception from: " + id);
        }, executionService);
        c.whenComplete((v, ex) -> {
            if(ex != null && canceled.compareAndSet(false, true)) {
                System.out.println("Canceling.");
                for(CompletableFuture<?> f: tasks) f.cancel(false);
                System.out.println("Canceled.");
            }
        });
    tasks.add(c);
    if(canceled.get()) {
        c.cancel(false);
        break;
    }
}

try {
    CompletableFuture.allOf(tasks.toArray(new CompletableFuture[0])).join();
} catch(Exception e) {
    System.out.println("Got async exception: " + e);
} finally {
    System.out.println("DONE");
}
executionService.shutdown();

Los runnables no se ejecutan una vez que su futuro asociado ha sido cancelada. Dado que no es una carrera entre la cancelación y la ejecución ordinaria, podría ser útil para cambiar la acción a

.runAsync(() -> {
    System.out.println("Running: " + id); 
    if(id == 400) throw new RuntimeException("Exception from: " + id);
    LockSupport.parkNanos(1000);
}, executionService);

para simular cierta carga de trabajo real. A continuación, se verá que menos acciones se ejecutan después de encontrarse con la excepción.

Desde la excepción asíncrona puede ocurrir incluso mientras que el bucle sometimiento todavía se está ejecutando, se utiliza una AtomicBooleanpara detectar esta situación y detener el bucle en esta situación.


Tenga en cuenta que para una CompletableFuture, no hay diferencia entre la cancelación y cualquier otro acabado excepcional. Llamando f.cancel(…)es equivalente a f.completeExceptionally(new CancellationException()). Por lo tanto, ya que CompletableFuture.allOflos informes de excepción en el caso excepcional, que será muy probable que una CancellationExceptionvez de la excepción de disparo.

Si reemplaza las dos cancel(false)llamadas con complete(null), se obtiene un efecto similar, los runnables no se ejecutan para los futuros ya realizados, pero allOfreportará la excepción original, ya que es la única excepción a continuación. Y tiene otro efecto positivo: completar con un nullvalor es mucho más barato que la construcción de una CancellationException(por cada futuro pendiente), por lo que la finalización forzada a través complete(null)corre mucho más rápido, evitando más futuros de la ejecución.

Supongo que te gusta

Origin http://43.154.161.224:23101/article/api/json?id=197617&siteId=1
Recomendado
Clasificación