2010-07-26 46 views
32

以下代码片段试图完成此操作。线程和执行程序的优雅关闭

该代码将永久循环并检查是否有任何待处理的请求需要处理。如果有的话,它会创建一个新线程来处理请求并将其提交给执行者。一旦所有线程完成,它会休眠60秒,并再次检查未决请求。

public static void main(String a[]){ 
    //variables init code omitted 
    ExecutorService service = Executors.newFixedThreadPool(15); 
    ExecutorCompletionService<Long> comp = new ExecutorCompletionService<Long>(service); 
    while(true){ 
     List<AppRequest> pending = service.findPendingRequests(); 
     int noPending = pending.size(); 
     if (noPending > 0) { 
      for (AppRequest req : pending) { 
       Callable<Long> worker = new RequestThread(something, req); 
       comp.submit(worker); 
      } 
     } 
     for (int i = 0; i < noPending; i++) { 
      try { 
       Future<Long> f = comp.take(); 
       long name; 
       try { 
        name = f.get(); 
        LOGGER.debug(name + " got completed"); 
       } catch (ExecutionException e) { 
        LOGGER.error(e.toString()); 
       } 
      } catch (InterruptedException e) { 
       LOGGER.error(e.toString()); 
      } 
     } 
     TimeUnit.SECONDS.sleep(60); 
    } 

    } 

我的问题是这些线程处理数据库的大部分处理。这个程序将运行在Windows机器上。当有人试图关闭或注销机器时,这些线程会发生什么?如何正常关闭正在运行的线程以及执行程序。

回答

55

一个ExecutorService的典型有序地关闭可能是这个样子:

final ExecutorService executor; 

Runtime.getRuntime().addShutdownHook(new Thread() { 
    public void run() { 
     executor.shutdown(); 
     if (!executor.awaitTermination(SHUTDOWN_TIME)) { //optional * 
      Logger.log("Executor did not terminate in the specified time."); //optional * 
      List<Runnable> droppedTasks = executor.shutdownNow(); //optional ** 
      Logger.log("Executor was abruptly shut down. " + droppedTasks.size() + " tasks will not be executed."); //optional ** 
     } 
    } 
}); 

*您可以登录执行者在等待你愿意等待的时间后仍然有任务要处理。
**您可以尝试强制执行程序的工作线程放弃当前的任务,并确保它们不会启动任何剩余的一些。

请注意,当用户向java进程发出中断或者ExecutorService仅包含守护进程线程时,上述解决方案将工作。相反,如果在ExecutorService包含未完成非守护线程时,JVM不会尝试关闭,因此关闭挂钩将不会被调用。

如果试图关机的处理作为分立应用程序生命周期(不是服务),那么关闭代码​​不应该被放置在关闭钩内的一部分,但是在该程序被设计成终止适当的位置。

+0

这是倒退。假设'ExecutorService'是由'Executors'工厂方法返回的典型值之一,背衬线程非守护线程。在这些线程退出之前,关闭钩子将不会被调用,并且直到“ExecutorService”关闭才会发生。只有在执行程序的线程不是守护进程或者程序收到用户中断的情况下,您的解决方案才会生效。 – 2015-10-07 01:11:17

+0

的OP大约是在JVM已经被要求执行有序关机的情况下专门询问。无论如何,答案的内容都是准确的,只需要在关闭钩子中不要这样做就可以提前终止执行程序服务。 – 2015-10-07 01:22:21

+0

非常感谢您的回答。我想我错过了他们问题的那一部分。你可以添加第三个***来澄清这一点吗? – 2015-10-07 01:24:28

7

书 “Java Concurrency in Practice” 规定:

7.4。 JVM关机

JVM可以以有序或突然的方式以 关闭。当最后一个 “正常”(nondaemon)线程 终止,有人调用System.exit, 或其他特定于平台的方式 (如发送SIGINT或打 按Ctrl-C)的有序 关机启动。 [...]

7.4.1。 Shutdown Hooks

在有序关闭中,JVM第一个 启动所有已注册的关闭挂钩。 关闭挂钩未登记的线程 已注册 Runtime.addShutdownHook。 JVM对 关闭挂钩的启动顺序无法保证 。如果任何 应用程序线程(守护进程或 非守护进程)仍在关闭时间 上运行,则它们将继续与关闭 进程同时运行 。当所有关闭挂钩已完成 时,如果runFinalizersOnExit为 为true,则JVM可能选择运行 终结器,然后暂停。 JVM不会让 尝试停止或中断仍在关机时运行的 的任何 应用程序线程;当JVM 最终停止时,它们是 突然终止。如果关闭 挂钩或终结器未完成,则 然后有序关闭进程 “挂起”,并且JVM必须突然关闭 。 [...]

重要的位, “的JVM没有试图阻止或中断仍在关机时运行的任何应用程序线程;当JVM最终停止,他们会突然终止”所以我想连接数据库会突然终止,如果没有关闭钩子在那里做一个优雅的清理(如果你使用的是框架,他们通常会提供这样的关闭钩子)。根据我的经验,到数据库的会话可以保留,直到数据库超时,等应用程序。没有这样的钩子而被终止。

2

您可以拨打shutdown()ExecutorService

发起一个有序的关闭,其中 以前提交的任务执行 ,但没有新的任务将 接受。

,或者您可以拨打shutdownNow()

尝试停止所有正在 执行任务,暂停等待任务的处理 ,并返回一个列表的那个正在等待 执行的任务 。

除 之外没有任何保证尽力尝试阻止 处理主动执行任务。 例如,典型的实现方式中 将通过了Thread.interrupt()取消,所以 失败向 响应中断可能永远不会终止任何任务。

调用哪一个取决于你想有多严重就停止....

+0

我在我的web应用程序中遇到以下问题。如何停止内存泄漏.http://stackoverflow.com/questions/39166512/memory-leak-error-in-tomcat-server-even-after-removed-quartz相关代码 – Vicky 2016-08-27 05:48:43

5

由于添加关机钩以明确调用shutdown()对我无效,所以我在Google的Guava中找到了一个简单的解决方案: com.google.common.util.concurrent.MoreExecutors.getExitingExecutorService

+1

不过,它有点问题,因为'MoreExecutors.getExitingExecutorService'只接受'ThreadPoolExecutor'实例。因此,您必须手动定义这些执行程序,而不是简单地调用,例如返回'ExecutorService'的'Executors.newFixedThreadPool'。 – voo 2013-07-12 14:53:11