2010-10-06 90 views
38

我有一个ScheduledThreadPoolExecutor,它似乎在吃异常。如果提交的Runnable引发异常,我希望执行程序服务通知我。ThreadPools中的异常处理

例如,我想下面的代码起码打印IndexArrayOutOfBoundsException的堆栈跟踪

threadPool.scheduleAtFixedRate(
    new Runnable() { 
    public void run() { 
     int[] array = new array[0]; 
     array[42] = 5; 
    } 
    }, 
    1000, 
    1500L, 
    TimeUnit.MILLISECONDS); 

作为一个方面的问题。有没有办法为ScheduledThreadPoolExecutor编写一个通用的try catch块?原来问题的

////////// END //////////////

由于提出了下列装饰效果很好。

public class CatcherTask implements Runnable{ 

    Runnable runMe; 

    public CatcherTask(Runnable runMe) { 
     this.runMe = runMe; 
    } 

    public void run() { 
     try { 
      runMe.run(); 
     } catch (Exception ex){ 
      ex.printStackTrace(); 
     } 
    } 
} 
+3

优秀的问题。我想知道为什么只有少数人遇到这种情况。 – whiskeysierra 2010-10-06 19:06:06

+0

另请参阅http://stackoverflow.com/questions/1687977/how-to-properly-catch-runtimeexceptions-from-executors – Raedwald 2014-04-29 15:57:33

回答

22

我写了一个关于这个问题的小post前一阵子。你有两个选择:

  1. 使用由科林·赫伯特提供的solution
  2. 使用马克彼得斯solution的修改版本,但不是分配UncaughtExceptionHandler你包装每个已提交的Runnable到一个可运行你自己的,其执行(调用run)真正可以在try-catch-block内运行。

编辑
正如指出的马克,包住Runnable传递给ScheduledExecutorService,而不是一个传递给ThreadFactory是很重要的。

+0

好吧,我确认并删除了我的答案。谢谢!随意修改您的第2步以了解详情。 – 2010-10-06 19:10:48

+2

其实我试过了#2,并且无法使它工作。异常被捕获到委托Runnable中的try/catch块上方,所以这似乎屈服于与UncaughtExceptionHandler相同的问题。 – 2010-10-06 19:19:07

+0

我刚刚接受了这个 - 但马克说,用try/catch装饰提交的任务不是....我会试试这个。 – Ivan 2010-10-06 19:23:16

12

警告:此方法不适用于计划线程池的执行者。这个答案因其与其他线程池执行者的相关性而被取消了。请参阅Willi's answer

覆盖的ThreadFactory给线程的UncaughtExceptionHandler的:

ThreadPoolExecutor exec = new ThreadPoolExecutor...; 

exec.setThreadFactory(new ExceptionCatchingThreadFactory(exec.getThreadFactory())); 
//go on to submit tasks... 


private static class ExceptionCatchingThreadFactory implements ThreadFactory { 
    private final ThreadFactory delegate; 

    private ExceptionCatchingThreadFactory(ThreadFactory delegate) { 
     this.delegate = delegate; 
    } 

    public Thread newThread(final Runnable r) { 
     Thread t = delegate.newThread(r); 
     t.setUncaughtExceptionHandler(new UncaughtExceptionHandler() { 
      @Override 
      public void uncaughtException(Thread t, Throwable e) { 
       e.printStackTrace(); //replace with your handling logic. 
      } 
     }); 
     return t; 
    } 
} 
+4

'UncaughExceptionHandler'不适用于计划的可运行参数。 – whiskeysierra 2010-10-06 18:54:10

+0

@Willi:让我验证一下,如果我确认,我会删除它。 – 2010-10-06 19:01:00

+1

取消删除,因为它对于非预定线程池执行程序似乎仍然有用。 – 2010-10-06 19:26:10

0

考虑您的的ScheduledThreadPoolExecutor添加静态事件类,如果有异常抛出您的任何任务都可以调用。这样,您可以利用该事件来捕获并处理线程中发生的异常。

+1

'ScheduledThreadPoolExecutor'在'java.util.concurrent'中...... – whiskeysierra 2010-10-06 19:01:37

5

您可以使用Future中的get()方法调用scheduleAtFixedRate()。如果在执行线程期间发生异常,它会抛出一个ExecutionException

+0

这对于后续调用是如何工作的?例如在第一次成功返回之后,你如何获得第二次调用的未来? – 2010-10-06 18:47:38

+0

@Mark如果一次执行失败,则不会执行第二次(或第三次,......)执行。从原始javadoc复制:*如果任务的任何执行遇到异常,则后续执行被抑制* – whiskeysierra 2010-10-06 19:04:26

+0

@Willi:那么我想我对OP的陈述“似乎在吃例外......”感到困惑,如果它是只运行一次。 – 2010-10-06 19:08:39

2

您也可以使用Spring Framework中的ThreadPoolTaskScheduler,它提供了一个设置错误处理程序的方法并为您执行所有打包。默认行为取决于任务的类型:

如果提供的ErrorHandler不为空,它将被使用。否则,默认情况下,重复任务的错误被抑制,而一次性任务的默认错误会传播,因为通过返回Future可能会出现这些错误。在这两种情况下,都会记录错误。

如果你只想使用包装一部分,而不是TaskScheduler可以使用

TaskUtils.decorateTaskWithErrorHandler(task, errorHandler, isRepeatingTask) 

其中TaskScheduler内部使用。

2

您可以对ScheduledThreadPoolExecutor进行子类化,并覆盖afterExecute方法来处理您提交的任何类型的Runnable的异常和错误。