Catching thread exceptions from Java ExecutorService

后端 未结 6 1065
闹比i
闹比i 2020-12-30 04:18

I\'m working on a software development framework for parallel computing JavaSeis.org. I need a robust mechanism for reporting thread exceptions. During development, knowing

相关标签:
6条回答
  • 2020-12-30 04:27

    As explained in this thread What is the difference between submit and execute method with ThreadPoolExecutor, using execute will only work if you implement Runnable and not Callable as execute cannot return a Future.

    I think in your scenario you should build the future object so that it can accommodate the exception stuff also. So in case of exception you build the error message object.

    0 讨论(0)
  • 2020-12-30 04:33

    Consider calling execute() instead of submit() on the ExecutorService. A Thread invoked with execute() will invoke the Thread.UncaughtExceptionHandler when it fails.

    Simply make a ThreadFactory that installs a Thread.UncaughtExceptionHandler on all Threads and then invoke your work with execute() on the ExecutorService instead of submit().

    Have a look at this related stack overflow question.

    0 讨论(0)
  • 2020-12-30 04:36

    My original question asked how to implement "robust" thread exception handling with Java ExecutorService. Thanks to Angelo and Greg for pointers on how exception handling works with ExecutorService.submit() and Future.get(). My revised code fragment is shown below. The key point I learned here is that Future.get() catches all exceptions. If the the thread was interrupted or cancelled, you get the appropriate exception, otherwise, the exception is wrapped and re-thrown as an ExecutionException.

    import java.util.concurrent.Callable;
    import java.util.concurrent.CancellationException;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
    
    public class TestThreadFailure {
    
      public static void main(String[] args) {
        int size = 1;
        ExecutorService exec = Executors.newFixedThreadPool(size);
        ThreadFailTask worker = new ThreadFailTask();
        Future result = exec.submit(worker);
        try {
          Integer value = result.get();
          System.out.println("Result: " + value);
        } catch (ExecutionException ex) {
          System.out.println("Caught failure: " + ex.toString());
          exec.shutdownNow();
          return;
        } catch (InterruptedException iex) {
          System.out.println("Thread interrupted: " + iex.toString());
        } catch (CancellationException cex) {
          System.out.println("Thread cancelled: " + cex.toString());
        }
        exec.shutdownNow();
        throw new RuntimeException("Did not catch failure !!");
      }
    
      public static class ThreadFailTask implements Callable {
        @Override
        public Integer call() {
          int nbuf = 65536;
          double[][] buf = new double[nbuf][nbuf];
          return new Integer((int) buf[0][0]);
        }
      }
    }
    
    0 讨论(0)
  • 2020-12-30 04:37

    I didn't have a great deal of luck with other answers because I needed the actual exception instance, itself, not just a printed stack trace. For me, the accepted answer involving ThreadPoolExecutor#afterExecute() of the question "Why is UncaughtExceptionHandler not called by ExecutorService?" worked.

    See the following sample code:

    List<Runnable> tasks = new LinkedList<>();
    for (int i = 0; i < numThreads; ++i) {
        Runnable task = new Runnable() {
            @Override
            public void run() {
                throw new RuntimeException();
            }
        };
    
        tasks.add(task);
    }
    
    Optional<Throwable> opEmpty = Optional.empty();
    /*
     * Use AtomicReference as a means of capturing the first thrown exception, since a
     * spawned thread can't "throw" an exception to the parent thread.
     */
    final AtomicReference<Optional<Throwable>> firstThrownException =
            new AtomicReference<>(opEmpty);
    
    /*
     * Use new ThreadPoolExecutor instead of Executors.newFixedThreadPool() so
     * that I can override afterExecute() for the purposes of throwing an
     * exception from the test thread if a child thread fails.
     */
    ExecutorService execSvc = new ThreadPoolExecutor(numThreads, numThreads,
                0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()) {
        @Override
        public void afterExecute(Runnable task, Throwable failureCause) {
            if(failureCause == null) {
                // The Runnable completed successfully.
                return;
            }
            // only sets the first exception because it will only be empty on the first call.
            firstThrownException.compareAndSet(Optional.<Throwable>empty(), Optional.of(failureCause));
        }
    };
    
    for (Runnable task : tasks) {
        execSvc.execute(task);
    }
    execSvc.shutdown();
    execSvc.awaitTermination(1, TimeUnit.HOURS);
    
    assertEquals(firstThrownException.get(), Optional.empty());
    
    0 讨论(0)
  • 2020-12-30 04:44

    I don't believe there is a standard 'hook' to get to these exceptions when using submit(). However, if you need to support submit() (which sounds reasonable, given that you use a Callable), you can always wrap the Callables and Runnables :

    ExecutorService executor = new ThreadPoolExecutor(1, 10, 60, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>()) {
        @Override
        public <T> Future<T> submit(final Callable<T> task) {
            Callable<T> wrappedTask = new Callable<T>() {
                @Override
                public T call() throws Exception {
                    try {
                        return task.call();
                    }
                    catch (Exception e) {
                        System.out.println("Oh boy, something broke!");
                        e.printStackTrace();
                        throw e;
                    }
                }
            };
    
            return super.submit(wrappedTask);
        }
    };
    

    Of course, this method only works if you're the one building the ExecutorService in the first place. Furthermore, remember to override all three submit() variants.

    0 讨论(0)
  • 2020-12-30 04:47

    To Handling exceptions in ExecutorService you have to take the advantage of Callable and Future.

    Callable is similar to Runnable and both are functional interface but run() of Runnable doesn't throws exception and the return type is void where as call() of Callable returns a generics and throws exception.

    Java-8 way:

    ExecuterService executor = null; 
    Future<Integer> future = null;
    
    Callable<Integer> yourTask = () -> {
        //your implementation here();
        //your implementation here();
    };
    
    try 
       {
            executor = Executors.newCachedThreadPool();
            future = executor.submit(yourTask );
            Integer result = future.get();
            System.out.println(result);
       }
       catch (ExecutionException | TimeoutException | InterruptedException e) 
       {
        // TODO: handle exception
       }
       finally 
       {
           executer.shutdown();
       }
    
    0 讨论(0)
提交回复
热议问题