CompletableFuture com Runnable-delegação - exceção é ignorada na classe delegando

Vishal:

Estou enfrentando um problema ao converter meu código para non-blocking código usando CompletableFuture. Para minimizar o alcance da questão, eu criei um código de exemplo que se comporta de forma diferente quando eu uso CompletableFuture. A questão é CompletableFuture engole a exceção de Runnable-delegação.

Eu estou usando delegação no topo de Runnable e ExecutorService para fornecer algum código invólucro necessário no meu aplicativo original.

Código de amostra:

  • MyRunnable: Meu executável amostra, que joga sempre a exceção.

    public class MyRunnable implements Runnable {
    
        @Override
        public void run() {
            System.out.println("This is My Thread throwing exception : " + Thread.currentThread().getName());
            throw new RuntimeException("Runtime exception from MyThread");
        }
    }
    
  • DelegatingRunnable - este é delegar executável que delegados e lógica envoltório ao redor do Runnable passado para ele, e espaço reservado para o tratamento de exceção.

    public class DelegatingRunnable implements Runnable {
    
        private Runnable delegate; 
    
        public DelegatingRunnable(Runnable delegate) {
            this.delegate = delegate;
        }
    
        @Override
        public void run() {
            System.out.println("Delegating Thread start : " + Thread.currentThread().getName());
            try {
                // Some code before thread execution
                delegate.run();
                // Some code after thread execution
            } catch (Exception e) {
                // While using CompletableFuture, could not catch exception here
                System.out.println("###### Delegating Thread Exception Caught : " + Thread.currentThread().getName());
                //throw new RuntimeException(e.getMessage());
            } catch (Throwable t) {
                System.out.println("!!!!!!! Delegating Thread Throwable Caught : " + Thread.currentThread().getName());
            }
            System.out.println("Delegating Thread ends : " + Thread.currentThread().getName());
        }
    
    }
    
  • DelegatingExecutorService - este delegados executar método. Ele só envolve o executável com DelegatingRunnable.

    public class DelegatingExecutorService extends AbstractExecutorService {
    
        private ExecutorService executor;
    
        public DelegatingExecutorService(ExecutorService executor) {
            this.executor = executor;
        }
    
        @Override
        public void execute(Runnable command) {
            executor.execute(new DelegatingRunnable(command));
        }
    
        // Othere delegating methods
    
    }       
    
  • MainClass - Eu estou usando duas abordagens. WAY1 - usando ExecutorService sem CompletableFuture. Way2 - usando CompletableFuture

    public class MainClass {
    
        public static void main(String[] arg) {
            //way1();
            way2();
        }
    
        public static void way2() {
            System.out.println("Way:2 # This is main class : " + Thread.currentThread().getName());
    
            ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()+1);
            DelegatingExecutorService executorService = new DelegatingExecutorService(executor);
    
            CompletableFuture.runAsync(new MyRunnable(), executorService)
                .whenComplete((res, ex) -> {
                    if (ex != null) {
                        System.out.println("whenComplete - exception  : " + Thread.currentThread().getName());
                    } else {
                        System.out.println("whenComplete - success  : " + Thread.currentThread().getName());
                    }
                });
    
            executor.shutdown();
            System.out.println("main class completed : " + Thread.currentThread().getName());
        }
    
        public static void way1() {
            System.out.println("Way:1 # This is main class : " + Thread.currentThread().getName());
            ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()+1);
    
            DelegatingExecutorService executorService = new DelegatingExecutorService(executor);
    
            executorService.execute(new MyRunnable());
    
            executor.shutdown();
            System.out.println("main class completed : " + Thread.currentThread().getName());
        }
    }
    

Pergunta: Quando eu executo WAY1 (), a saída é

    Way:1 # This is main class : main
    Delegating Thread start : pool-1-thread-1
    This is My Thread throwing exception : pool-1-thread-1
    ###### Delegating Thread Exception Caught : pool-1-thread-1
    main class completed : main
    Delegating Thread ends : pool-1-thread-1

Você pode notar que bloco catch de 'DelegatingRunnable' pode capturar a exceção aqui, que é levantada a partir de MyRunnable. Mas se eu usar WAY2 () usando CompletableFuture, a exceção de MyRunnable não é cought sob DelegatingRunnable, embora eu ver ele está sendo tosse sob 'whenComplete' callback de CompletableFuture.

Saída de WAY2 é

    Way:2 # This is main class : main
    Delegating Thread start : pool-1-thread-1
    This is My Thread throwing exception : pool-1-thread-1
    Delegating Thread ends : pool-1-thread-1
    whenComplete - exception  : main
    main class completed : main

Você pode notar que o CompletableFuture está usando o mesmo DelegatingExecutionService e DelegatingRunnable internamente. Eu não entendo por que DelegatingRunnable não pode capturar a exceção neste caso.

(Por que eu estou usando CompletableFuture -? Este é apenas um código de exemplo para explicar o problema exato que eu estou enfrentando Mas no geral, eu preciso usar CompletableFuture para fazer cadeia de tarefa evantually de forma não-bloqueio).

Herói Wanders:

No código-fonte de CompletableFutureque você pode ver que ele envolve o dado Runnableem um objeto do tipo AsyncRunque se implementa Runnable. Este AsyncRunserá passado para o seu executor executemétodo. Quando o interior / original Runnablegera uma exceção, ele é pego pelo código de AsyncRune CompletableFutureé completado como falhou, mas a exceção será não se re-lançada.

É por isso que o seu envoltório ( DelegatingRunnable) nunca verá a exceção.

Acho que você gosta

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