2016-09-28 37 views
3

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

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

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

看起来好像结果应该是我的预期:抛出错误的线程终止,显示消息。事实证明,当一个Runnable传递给ExecutorService使用submit(Runnable)时,它被封装在RunnableFuture<Void>不处理错误,我找不到方法来改变这种行为,而不是直接调用execute(Runnable),由于某种原因doesn没有表现出相同的行为。

对此有没有“最佳实践”?如果我知道一个线程可能会抛出一个错误,是否有一种方法来submit它到一个ExecutorService而不是吞下该错误?

+0

在小样式的笔记中,您应该避免抛出错误。它们表明严重的问题,您不应该尝试处理(例如内存不足)。如果你想从'Runnable'中抛出一些东西,你应该使用'RuntimeException'(或其子类)。 –

+0

[处理来自Java ExecutorService任务的异常]可能的重复(http://stackoverflow.com/questions/2248131/handling-exceptions-from-java-executorservice-tasks) –

+1

只是为了确保,'RunnableFuture'_handlers_错误正好。所有可运行的调用都被封装在try/finally中。你的意思是说你看不到我怀疑的错误信息。 – Gray

回答

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) 
+0

你确定一个错误将被捕获,如果它说它会抛出异常? – ndm13

+1

@ ndm13是的,它会是:“java.util.concurrent.ExecutionException:java.lang.Error:sample error”。我已经更新了答案。 – MGorgon

+1

@ ndm13我已经更新了在这里使用普通Runnable的答案。错误可以从它抛出而不抛出声明。 – MGorgon

0

一个Future是代表运行你的操作,无论是准备的结果,现在或在某一时刻的对象在将来。一般来说,为了得到结果,人们会调用get方法,特别是如果您将Callable而不是Runnable传入ExecutorService

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

想象它像一个音乐场地的外套检查。如果他们失去了你的外套,他们可能不会告诉你,直到你拿着你的外套要求你的外套。 Future为您的票的目的在这里。

1

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

从Java文档:“一个错误是Throwable的子类,表示严重的问题,合理的应用程序不应该试图大多数这样的错误都是异常情况,ThreadDeath错误虽然是一个“正常”条件,但也是Error的一个子类,因为大多数应用程序不应该试图捕获它。

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

+0

因此,从本质上讲,最好的做法是引发RuntimeException并像处理其他异常一样处理它,即使Error通常会抛出,如果它在线程内? – ndm13

+1

Personnally我没有找到任何理由为什么你应该在你的代码中抛出错误。 现在,如果您需要使用您不负责的代码(例如第三方),则会抛出错误,问题是:我应该捕捉它还是不知道。这是一个个案的事情,但我会说,如果它的原因是可恢复的,你可以捕获错误。 例如,假设您的Web应用程序使用第三方,如速度由于无效模板而引发错误,我相信您应该捕获并处理错误,因为Web服务可以继续运行。 –