【问题标题】:How does Java handle Errors in multithreading?Java如何处理多线程中的错误?
【发布时间】:2017-02-07 01:46:23
【问题描述】:

我正在处理一个多线程项目,其中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();
    }
}

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

对此有“最佳实践”吗?如果我知道线程可能会抛出错误,有没有办法将 submit 发送到 ExecutorService 而不会吞下错误?

【问题讨论】:

  • 在小风格笔记上,您应该避免抛出Errors。它们表明您无论如何都不应该尝试处理的严重问题(例如内存不足)。如果你想从Runnable 中抛出一些东西,你应该改用RuntimeException(或其子类)。
  • 只是为了确保RunnableFuture 处理错误就好了。所有可运行的调用都包含在 try/finally 中。你的意思是说你看不到我怀疑的错误信息。

标签: java multithreading error-handling


【解决方案1】:

是的,将您的任务提交到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)

【讨论】:

  • 如果说抛出异常,你确定会捕获一个错误吗?
  • @ndm13 是的,应该是:“java.util.concurrent.ExecutionException: java.lang.Error: sample error”。我已经更新了答案。
  • @ndm13 我已更新答案以在此处使用普通的 Runnable。可以在没有 throws 声明的情况下从中抛出错误。
  • 通常我们使用 ExecutionException 的 getCause() 方法,这在 javadocs 中有说明。
【解决方案2】:

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

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

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

【讨论】:

  • 所以基本上,最好的做法是抛出一个 RuntimeException 并像处理任何其他异常一样处理它,即使通常会抛出一个错误,如果它在线程内?
  • 就我个人而言,我找不到任何应该在代码中抛出错误的理由。现在,如果您需要使用您不负责的代码,例如第三方,会引发错误,问题是:我应该抓住它还是不抓住它。这是一个个案,但我会说如果它的原因是可恢复的,你可以捕捉到错误。例如,假设您的 Web 应用程序使用了诸如velocity 之类的第三方,由于模板无效而引发了错误,我认为您应该捕获并处理该错误,因为Web 服务可以继续运行。
【解决方案3】:

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

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

把它想象成在音乐场所检查外套。如果他们丢了你的外套,他们可能不会告诉你,直到你拿着票来要你的外套。 Future 在这里为您的票证服务。

【讨论】:

    猜你喜欢
    • 2013-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-11
    • 2020-01-12
    相关资源
    最近更新 更多