正确关闭线程和执行程序

use*_*101 34 java multithreading

下面这段代码试图帮助这个.

代码永远循环并检查是否有任何待处理的请求要处理.如果有,则创建一个新线程来处理请求并将其提交给执行程序.完成所有线程后,它会休眠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);
    }

  }
Run Code Online (Sandbox Code Playgroud)

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

Tim*_*der 61

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 **
        }
    }
});
Run Code Online (Sandbox Code Playgroud)

*您可以在等待您愿意等待的时间后记录执行程序仍有任务要处理.
**您可以尝试强制执行程序的工作线程放弃当前任务,并确保它们不启动任何其余任务.

请注意,当用户向您的java进程发出中断或您的ExecutorService唯一包含守护程序线程时,上述解决方案将起作用.相反,如果ExecutorService包含尚未完成的非守护程序线程,则JVM将不会尝试关闭,因此不会调用关闭挂钩.

如果尝试将进程作为离散应用程序生命周期(而不是服务)的一部分关闭,则不应将关闭代码放在关闭挂钩内,而应放在程序设计为终止的适当位置.

  • 这是倒退的。假设“ExecutorService”是“Executors”工厂方法返回的典型值之一,则支持线程是非守护线程。在这些线程退出之前,不会调用关闭挂钩,并且在“ExecutorService”关闭之前不会发生这种情况。仅当执行器的线程是非守护程序或者您的程序收到用户中断时,您的解决方案才会有效。 (2认同)

Enn*_*oji 7

" Java Concurrency in Practice " 一书指出:

7.4.JVM关闭

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

7.4.1.关机钩

在有序关闭中,JVM首先启动所有已注册的关闭挂钩.关闭挂钩是使用Runtime.addShutdownHook注册的未启动线程.JVM不保证启动关闭挂钩的顺序.如果任何应用程序线程(守护程序或非守护程序)仍在关闭时运行,它们将继续与关闭进程同时运行.当所有关闭挂钩都已完成时,如果runFinalizersOnExit为true,则JVM可以选择运行终结器,然后停止.JVM不会尝试停止或中断在关闭时仍在运行的任何应用程序线程; 当JVM最终停止时,它们会突然终止.如果关闭挂钩或终结器没有完成,那么有序关闭进程"挂起" 并且必须突然关闭JVM.[...]

重要的一点是, "JVM不会尝试停止或中断任何仍在关闭时运行的应用程序线程;当JVM最终停止时,它们会突然终止." 所以我想给DB连接会突然终止,如果没有关闭挂钩在那里做一个优雅的清理(如果你使用框架时,通常都会提供这样的关闭挂钩).根据我的经验,数据库的会话可以保留,直到应用程序的数据库等等超时.没有这样的钩子就终止了.


小智 5

由于添加一个关闭钩子来显式调用shutdown()对我来说不起作用,我在Google的Guava中找到了一个简单的解决方案: com.google.common.util.concurrent.MoreExecutors.getExitingExecutorService.