我正在使用多个线程对大型数据集执行一些繁重(且容易出错)的处理。在程序可以继续之前,我要求所有线程完成执行,无论它们是抛出异常还是正常终止(没有返回值)。我使用CountDownLatch
来实现此目的,并使用ExecutorService
来实际运行作业。我希望工作线程(让我们称之为JobManager
- s为了参数)通知锁存器,即使它们抛出异常。 JobManager
可能需要一到两个小时才能完成,并且可能随时失败。如果抛出异常,我们的想法是调用JobManager
的“终结器”方法。现在,ExecutorService
喜欢捕捉异常或隐藏其不存在的真实起源。我有几种解决方法,但都不令人满意:
使用ExecutorService#execute(Runnable r)
而不是submit(Runnable r)
。我可以这样做,因为我不关心JobManager
的返回值。我提供了一个自定义ThreadFactory
,它为每个新创建的线程附加UncaughtExceptionHandler
。这种方法的问题在于,当UncaughtExceptionHandler#uncaughtException(Thread t, Throwable e)
被调用时,t
的{{1}}类型为Runnable
,而不是类型ThreadPoolExecutor$Worker
,这会阻止我从调用“终结者”方法。
使用自定义JobManager
并覆盖ExecutorService
方法。这与1的问题相同。
将整个afterExecute(Runnable r, Throwable t)
包装在JobManager#doWork()
语句中,并使用返回值指示是否抛出了异常。然后我可以catch
作业并使用submit
来决定是否抛出异常。我不喜欢这个解决方案,因为当你有一个精心设计的异常机制时,我觉得返回代码是错误的工具。此外,FutureTask#get()
将等待(除非被中断),这意味着我无法立即处理其他线程中的错误。
摆脱get()
。将所有CountDownLatch
存储在列表中并反复戳入,直到我对状态感到满意为止。这可能有用,但感觉就像是一个肮脏的黑客。
非常感谢任何建议。
答案 0 :(得分:4)
据我了解,您可以使用简单的try
- finally
块:
public class JobManager {
public void doWork() {
try {
...
} finally {
countDownLatch.countDown();
}
}
}