2011-01-05 53 views
0

我已经看到了一些关于这些问题的讨论,但并没有对我的问题给出具体的答案。由于未捕获的异常而导致线程死亡时,我想重新启动任务。在死亡线程中设置的UncaughtExceptionHandler内调用pool.execute(runnable)是否安全?理想情况下,如果throwable是RuntimeException,我想只重新提交runnable到池中,例如,从UncaughtExceptionHandler中重新执行任务?


pool = Executors.newFixedThreadPool(monitors.size(), new ThreadFactory() { 
    @Override 
    public Thread newThread(Runnable r) { 
     Thread thread = new Thread(r); 
     threadMap.put(thread, (Monitor)r); 
     thread.setName(((Monitor)r).getClusterName() + "-monitor"); 
     thread.setUncaughtExceptionHandler(new UncaughtExceptionHandler() { 
      @Override 
      public void uncaughtException(Thread th, Throwable t) { 
       logger.error("Uncaught exception in thread: " + th.getName(), t); 
       if (t instanceof RuntimeException) { 
        Monitor m = threadMap.get(th); 
        if (m != null && m.runCount() < restartMax) { 
         logger.error("Restarting monitor due to uncaughtException: " + m.getClusterName()); 
         pool.execute(m); 
        }      } 
      }     
     }); 
     return thread; 
    }   
}); 

有没有更好的方法或更安全的方法来做到这一点?

谢谢!

回答

1

最安全的选择是只抛出运行时异常,这是致命的。如果运行时异常可以安全地忽略,为什么它不被捕获并继续?

看起来你的线程映射就像一个ThreadLocal,看起来一旦一个任务使用了你所有的restartMax,它将永远不会再重新启动一个任务?

我会这样做的方式是包装正在执行的Runnable。

public void submit(final Runnable runnable, final int restartMax) { 
    pool.submit(new Runnable() { 
     public void run() { 
      for(int i=0;i<restartMax;i++) 
       try { 
        runnable.run(); 
        break; 
       } catch (Exception e) { 
        log.error("Exception", e); 
       } 
     } 
    } 
} 
+0

threadMap只是为了让我得到对应于Thread的Monitor/Runnable异常。 restartMax只是为了避免无限重启立即遇到一些持续性错误的线程。该机制旨在从一些尚未明确处理的瞬态异常中恢复。 – batkins 2011-01-07 13:53:23

0

您的代码示例不会执行您尝试解决的工作。传递给ThreadFactory的runnable不是您的可运行任务,而是由ThreadPoolExecutor使用的内部runnable。

您可能需要考虑覆盖afterExecute()方法。此方法将始终被调用,并且第一个参数将是您的可运行的,第二个(Throwable)参数将包含未捕获的异常。然而,afterExecute()将而不是报告该异常,如果任务由FutureTask显式包装或通过submit()间接包装。因此,afterExecute()只能通过execute()提交。

protected void afterExecute(Runnable r, Throwable t) { 
    super.afterExecute(r, t); 
    if (t != null) { 
     Monitor m = (Monitor)r; 
     if (m.runCount() < restartMax) { 
      logger.error("Restarting monitor due to uncaughtException: " 
        + m.getClusterName()); 
      execute(m); // exception handling omitted 
     } 
    } 
} 
+0

是的,我立即遇到了这个问题。目前我正在使用传统的新线程/ setUncaughtExceptionHandler/start序列。看起来既奇怪又短视,您无法从线程访问Runnable。我会研究你的建议。我没有使用FutureTask也没有提交,所以这应该工作。谢谢! – batkins 2011-01-07 13:48:26

+0

即使线程是通过传入的runnable创建的,请注意,它仅仅是触发线程创建的第一个**任务。线程将会长期运行并运行多个任务。因此,您将无法以可靠的方式获得导致RuntimeException的实际运行。 – sjlee 2011-01-07 16:52:53