当工作线程有异常时关闭ThreadPoolExecutor

时间:2013-12-02 11:37:17

标签: java multithreading threadpoolexecutor

工作线程发出应该启动正常关闭的最佳方式是什么?

我有一个固定大小的线程池,它通过一组连续的任务工作,每个任务持续不超过几秒钟。在正常操作期间,这种方法很好,并且随着工作量的增加而突出。

我遇到的问题是在其中一个线程中抛出异常。如果发生这种情况,我想把整个事情搞砸,并且无法正常工作。

目前的方法

我一直使用的天真方法是在“Supervisor”类中使用静态方法,该方法使用标准shutdown()awaitTermination()方法关闭线程池。如果遇到问题,则由任何“Worker”类调用它。完成此操作而不是传播异常,因为execute()需要Runnablerun()方法不能抛出异常。

这是一些伪代码:

// Finds work to do and passes them on to workers
class Supervisor {

  ThreadPoolExecutor exec;

  static main() {
    exec = new FixedThreadPool(...);
    forever {
      exec.execute(new Worker(next available task));
     }
  }

  static stopThreadPool() {
    exec.shutdown();
    if(!exec.awaitTermination(timeout_value)) {
      print "Timed out waiting on terminate"
    }
  }
}


class Worker {
  run() {
    try {
      // Work goes here
    } catch () {
      Supervisor.stopThreadPool()
    }
  }
}

我看到的效果是线程暂停了一段时间,但后来我看到了超时消息,他们都恢复了处理。这种模式一直持续到我手动关闭它为止。如果在断开循环后在main方法中调用stopThreadPool(),则按预期正确关闭。

这种方法显然是错误的,因为它不起作用,但它也感觉就像设计不对。

重申一个问题:工作线程发出应该启动正常关闭的最佳方式是什么?

其他信息

我在SO上看到的问题有两种类型:

  • “如何杀死线程池中的线程?”
  • “我怎么知道我的所有线程都已完成?”

那不是我追求的。他们似乎也只谈论一组有限的任务,而我正在处理一个连续的饲料。

我已经阅读过使用exec.submit()Future的另一种方法,它将责任放在主管类上以检查一切是否正常,但我不知道它是否知道它是否是一个更好的设计。例外情况是,例外情况,所以我不想在不必要的情况下为正常情况添加工作/复杂性。

(小方注意:这是一个工作项目,还有其他人参与。为简单起见,我在问题中说“我”。)

2 个答案:

答案 0 :(得分:4)

您距离正确的解决方案还不远,问题是您需要正确处理由关闭调用引起的中断。所以你的线程的run方法应如下所示:

run () {
  try {
    while (Thread.interrupted() == false) {
      doSomeWork();
    }
  } catch (Exception e) { 
    myExecutor.shutdown();
  }
}

请注意,我明确地使用了shutdown()而没有awaitTermination(),否则等待线程会使Executor无法正常终止,因为一个线程仍在等待。完美的单线程死锁。 ;)

中断检查是关于如何正常杀死线程的提示:通过将running布尔值设置为false或通过中断来使run方法结束,线程将在片刻之后死亡

要检查所有线程是否已终止(=即将结束其运行方法),您可以将CountDownLatch用于简单的情况,或使用CyclicBarrier / Phaser类来处理更复杂的情况。

答案 1 :(得分:1)

这里有两个问题:

  1. 如果您只想强制关闭工作程序中的任何异常,那么您可以使用shutdown()并等待对应方。只需使用shutdownNow强制它,你应该很好。关机会正常关机。

  2. 尝试在发生这样的事情时打破你的for循环。最好的方法是在执行调用的for循环中使用try catch。当一个工程中发生异常时抛出一个未经检查的异常并在for循环中捕获它。终止for循环并调用您的方法强制关闭执行程序。这是一种更清洁的方法。或者,您也可以考虑使用执行程序中的处理程序来执行此操作。