Java如何处理多线程中的错误?

时间:2016-09-28 21:59:45

标签: java multithreading error-handling

我正在处理多线程项目,其中Thread可能会抛出Error(而不是Exception)。没有找到关于如何在多线程中处理错误的任何可靠信息,我决定做一些测试,发现结果可能不一致。

这是我的测试代码,以及评论结果。

public class MultiThreadError {
    public static class ErrorThrowingRunnable implements Runnable{
        private final boolean throwsError;
        public ErrorThrowingRunnable(boolean throwsError){
            this.throwsError = throwsError;
        }

        @Override
        public void run() {
            try {
                // Wait between .5 and 1.5 seconds
                Thread.sleep(500 + new Random().nextInt(1000));
            } catch (InterruptedException ex) {}
            if(throwsError){
                throw new Error(Thread.currentThread().getName());
            }else{
                System.out.println(Thread.currentThread().getName());
            }
        }
    }

    public static void regularThreadPool(){
        // Crashes individual thread; swallows error
        ExecutorService threadPool = Executors.newFixedThreadPool(5);
        threadPool.submit(new ErrorThrowingRunnable(false));
        threadPool.submit(new ErrorThrowingRunnable(false));
        threadPool.submit(new ErrorThrowingRunnable(false));
        threadPool.submit(new ErrorThrowingRunnable(false));
        threadPool.submit(new ErrorThrowingRunnable(true));
        threadPool.shutdown();
    }

    public static void onDemandThreads(){
        // Crashes individual thread; displays error
        new Thread(new ErrorThrowingRunnable(false)).start();
        new Thread(new ErrorThrowingRunnable(false)).start();
        new Thread(new ErrorThrowingRunnable(false)).start();
        new Thread(new ErrorThrowingRunnable(false)).start();
        new Thread(new ErrorThrowingRunnable(true)).start();
    }

    public static void onDemandThreadPool(){
        // Same as onDemandThreads()
        ExecutorService threadPool = Executors.newFixedThreadPool(5);
        threadPool.execute(new ErrorThrowingRunnable(false));
        threadPool.execute(new ErrorThrowingRunnable(false));
        threadPool.execute(new ErrorThrowingRunnable(false));
        threadPool.execute(new ErrorThrowingRunnable(false));
        threadPool.execute(new ErrorThrowingRunnable(true));
        threadPool.shutdown();
    }

    public static void tooSmallThreadPool(){
        // When an error is thrown, apparently the thread that threw
        // the error is not reused, reducing the pool size
        ExecutorService threadPool = Executors.newFixedThreadPool(3);
        threadPool.execute(new ErrorThrowingRunnable(true));
        threadPool.execute(new ErrorThrowingRunnable(false));
        threadPool.execute(new ErrorThrowingRunnable(false));
        threadPool.execute(new ErrorThrowingRunnable(false));
        threadPool.execute(new ErrorThrowingRunnable(false));
        threadPool.execute(new ErrorThrowingRunnable(false));
        threadPool.execute(new ErrorThrowingRunnable(false));
        threadPool.execute(new ErrorThrowingRunnable(false));
        threadPool.shutdown();
    }
}

似乎结果应该是我所期望的:抛出错误的线程终止,显示消息。事实证明,当使用RunnableExecutorService传递给submit(Runnable)时,它会被RunnableFuture<Void>包裹起来并且不会处理错误,并且除了直接调用execute(Runnable)之外,我无法找到改变此行为的方法,而submit由于某种原因没有表现出相同的行为。

是否有最佳实践&#34;为了这?如果我知道一个Thread可能会抛出一个错误,有没有办法{{1}}它到ExecutorService而不是吞下错误?

3 个答案:

答案 0 :(得分:5)

是的,将您的任务提交至ExecutorService并检查返回Future的结果。

使用时:

ExecutorService es = Executors.newFixedThreadPool(1);

Future<?> result = es.submit(new Runnable() {
    @Override
    public void run() {
        throw new Error("sample error");
    }
});

try {
    result.get();
} catch (ExecutionException e) {
    e.printStackTrace();
}

您的堆栈跟踪将包含:

java.util.concurrent.ExecutionException: java.lang.Error: sample error
    at java.util.concurrent.FutureTask.report(Unknown Source)
    at java.util.concurrent.FutureTask.get(Unknown Source)
    at jjj.b.B.main(B.java:23)
Caused by: java.lang.Error: sample error
    at jjj.b.B$1.call(B.java:18)
    at jjj.b.B$1.call(B.java:1)
    at java.util.concurrent.FutureTask.run(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)

答案 1 :(得分:1)

尝试处理从线程抛出的错误似乎是一个可疑的用例......

来自Java Doc:&#34;错误是Throwable的子类,表示合理的应用程序不应该尝试捕获的严重问题。大多数此类错误都是异常情况。 ThreadDeath错误,虽然&#34;正常&#34; condition,也是Error的子类,因为大多数应用程序都不应该尝试捕获它。&#34;

我需要处理错误的唯一一次是当我使用严重管理其异常的第三方时,抛出Error而不是RuntimeException。在这种特定情况下,您需要捕获错误(或Throwable)以避免意外的应用程序崩溃。

答案 2 :(得分:0)

Future是一个对象,表示运行您的操作的结果,无论是现在还是将来某个时候。通常,人们会在所述未来调用get方法以获得结果,特别是如果您将Callable而不是Runnable传递到ExecutorService。< / p>

如果您的Runnable / Callable引发异常,则会反映在Future对象中。您可以通过调用Runnable方法来测试get是否成功运行。如果它是一个干净的运行,你将得到(在这种情况下)null。如果抛出异常,则get将抛出ExecutionException,并将该异常标记为其原因。

把它想象成一个音乐场地的外套。如果他们丢了你的外套,他们可能不会告诉你,直到你拿到你的外套要求你的外套。 Future符合您的机票目的。