从 UncaughtExceptionHandler 中重新执行任务?

Posted

技术标签:

【中文标题】从 UncaughtExceptionHandler 中重新执行任务?【英文标题】:Reexecute task from within UncaughtExceptionHandler? 【发布时间】:2011-06-04 03:33:35 【问题描述】:

我看到了一些类似的讨论,但没有具体回答我的问题。当线程由于未捕获的异常而死亡时,我想重新启动任务。在即将死去的线程上设置的 UncaughtExceptionHandler 中调用 pool.execute(runnable) 是否安全?

理想情况下,如果 throwable 是 RuntimeException,我只想将 runnable 重新提交到池中,例如


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

【讨论】:

threadMap 只是为了让我得到与异常对应的 Thread 的 Monitor/Runnable。 restartMax 只是为了避免无限重启立即遇到一些持久错误的线程。这种机制旨在从一些尚未明确处理的临时异常中恢复。【参考方案2】:

您的代码示例无法完成您尝试解决的工作。传递给 ThreadFactory 的可运行对象不是您的任务可运行对象,而是 ThreadPoolExecutor 使用的内部可运行对象。

您可能需要考虑重写 afterExecute() 方法。此方法将始终被调用,第一个参数将是您的可运行文件,第二个(Throwable)参数将包含未捕获的异常。但是,如果任务由您使用 FutureTask 显式包装或通过 submit() 间接包装,则 afterExecute() 将报告异常。因此,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
        
    

【讨论】:

是的,我立即遇到了那个确切的问题。目前我正在使用传统的新 Thread/setUncaughtExceptionHandler/start 序列。您无法从 Thread 访问 Runnable 似乎既奇怪又短视。我会研究你的建议。我没有使用 FutureTask 也没有提交,所以这应该可以。谢谢! 即使线程是通过传入的 runnable 创建的,请注意它只是触发线程创建的 第一个 任务。线程将长期存在并运行多个任务。因此,您将无法可靠地获取导致 RuntimeException 的实际可运行对象。

以上是关于从 UncaughtExceptionHandler 中重新执行任务?的主要内容,如果未能解决你的问题,请参考以下文章

从PRISM开始学WPFMVVMViewModel?

在 python 中,为啥从数组读取比从列表读取慢?

从图库中挑选或从相机捕获的高质量图像

从PRISM开始学WPFMVVMCommand?

从PRISM开始学WPFPrism?

mysql 主-主-从-从