从 Java ExecutorService 捕获线程异常

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4492273/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 18:00:57  来源:igfitidea点击:

Catching thread exceptions from Java ExecutorService

javamultithreadingexception

提问by Chuck Mosher

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 where exceptions came from has high value, so I would like to err on the side of over-reporting. I would also like to be able to handle Junit4 testing in threads as well. Is the approach below reasonable or is there a better way ?

我正在开发用于并行计算的软件开发框架JavaSeis.org。我需要一个强大的机制来报告线程异常。在开发过程中,知道异常来自哪里具有很高的价值,所以我想在过度报告方面犯错。我还希望能够在线程中处理 Junit4 测试。下面的方法是合理的还是有更好的方法?

import java.util.concurrent.Callable;
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<Integer> result = exec.submit(worker);
    try {
      Integer value = result.get();
      System.out.println("Result: " + value);
    } catch (Throwable t) {
      System.out.println("Caught failure: " + t.toString());
      exec.shutdownNow();
      System.out.println("Stack Trace:");
      t.printStackTrace();
      return;
    }
    throw new RuntimeException("Did not catch failure !!");
  }

  public static class ThreadFailTask implements Callable<Integer> {
    @Override
    public Integer call() {
      int nbuf = 65536;
      double[][] buf = new double[nbuf][nbuf];
      return new Integer((int) buf[0][0]);
    }
  }
}

采纳答案by Angelo van der Sijpt

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 :

我不相信在使用submit(). 但是,如果您需要支持submit()(这听起来很合理,因为您使用了 a Callable),您始终可以包装 Callables 和 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 ExecutorServicein the first place. Furthermore, remember to override all three submit()variants.

当然,这种方法只有在您是第一个构建的人时才有效ExecutorService。此外,请记住覆盖所有三个submit()变体。

回答by Greg Mattes

Consider calling execute()instead of submit()on the ExecutorService. A Threadinvoked with execute()will invoke the Thread.UncaughtExceptionHandlerwhen it fails.

考虑调用execute()而不是submit()ExecutorService. 一个Thread与调用execute()将调用Thread.UncaughtExceptionHandler时失败。

Simply make a ThreadFactorythat installs a Thread.UncaughtExceptionHandleron all Threadsand then invoke your work with execute()on the ExecutorServiceinstead of submit().

只需制作一个在所有ThreadFactory上安装 a 的,然后使用on而不是调用您的工作。Thread.UncaughtExceptionHandlerThreadsexecute()ExecutorServicesubmit()

Have a look at this related stack overflow question.

看看这个相关的堆栈溢出问题

回答by Fazal

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.

正如这个线程中所解释的ThreadPoolExecutor 的 submit 和 execute 方法有什么区别,只有在实现 Runnable 而不是 Callable 时使用 execute 才有效,因为 execute 不能返回 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.

我认为在您的场景中,您应该构建未来对象,以便它也可以容纳异常内容。因此,如果出现异常,您将构建错误消息对象。

回答by Chuck Mosher

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.

我最初的问题是问如何使用 Java ExecutorService 实现“强大的”线程异常处理。感谢 Angelo 和 Greg 提供有关异常处理如何与 ExecutorService.submit() 和 Future.get() 一起工作的指针。我修改后的代码片段如下所示。我在这里学到的关键点是 Future.get() 捕获所有异常。如果线程被中断或取消,您将获得相应的异常,否则,该异常将被包装并作为 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]);
    }
  }
}

回答by lambodar

To Handling exceptions in ExecutorServiceyou have to take the advantage of Callableand Future.

要处理ExecutorService 中的异常,您必须利用CallableFuture

Please watch the below video for more details. Hope this this will help you our.

请观看以下视频了解更多详情。希望这会帮助你我们的。

VIDEO: Callable and Future(11 min)

视频:Callable 和 Future(11 分钟)

回答by entpnerd

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.

我对其他答案的运气并不好,因为我需要实际的异常实例本身,而不仅仅是打印的堆栈跟踪。对我来说,关于问题“为什么 UncaughtExceptionHandler 没有被 ExecutorService 调用?”的ThreadPoolExecutor#afterExecute()的公认答案有效。

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());