Java并发:运行直到终止

Java并发:运行直到终止,java,concurrency,terminate,scheduledexecutorservice,shutdown-hook,Java,Concurrency,Terminate,Scheduledexecutorservice,Shutdown Hook,我目前正在重构一段代码,大致如下所示: public static void main(String... args) { while (true) { // Do something... Thread.sleep(300000); // Every 5 minutes } } 我想使用ScheduledExecutorService来运行代码: public static void main(String... args) { Sch

我目前正在重构一段代码,大致如下所示:

public static void main(String... args) {
    while (true) {
        // Do something...
        Thread.sleep(300000); // Every 5 minutes
    }
}
我想使用
ScheduledExecutorService
来运行代码:

public static void main(String... args) {
    ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
    service.scheduleAtFixedRate(new Task(...), 0, 5, TimeUnit.MINUTES);
}
不过,我对此有一些担心:首先,程序在调度后“终止”(如
main()
返回),但JVM保持活动状态,因为
ScheduledExecutorService
仍在运行。我相信这是故意的,为了保持旧代码的“一直运行直到从外部终止”行为,但我相信我必须安装一个关闭挂钩,以确保
ScheduledExecutorService
正确关闭。是真的会这样,还是“从外部杀死”也会阻止关闭钩子的执行

第二,我也对一开始就有一个关机挂钩感到不安。如果我有这样的代码

try {
    while (true) {}
} finally {
    // Shutdown hook code goes here instead
    service.shutdown();
    if (!service.awaitTermination(1, TimeUnit.SECONDS)) {
        service.shutdownNow();
    }
}
同样的“从外部被杀”的担忧是否仍然存在


编辑:是的,原始程序循环通过,但从未中断循环;它只意味着从SIGINT(终端中的control-C)终止。这是一个定义很差的终止条件,但这是我现在唯一拥有的…

根据设计,默认情况下,执行器在非守护进程线程中运行。这意味着您需要某种方式来通知执行者终止,否则您的程序将无法退出


您需要考虑在什么情况下希望退出程序。然后,在这种情况下,安排执行器关闭。

当Java进程被终止时,线程将不会收到异常;他们会突然死去。所以你的
最终
方法不会有帮助。关闭钩子将运行,但如果你被SIGKILL杀死,则不会运行。然而,关闭钩子将不能做很多事情,因为那时
ExecutorService
的池线程可能已经死了

换句话说,您最好寻找一种更高级的方法来要求Java程序完成


请记住,具有无限循环的当前代码与基于
ExecutorService
的解决方案在终止信号时的行为处于同等地位。

您所展示的两种方法之间存在根本性差异。
while(true)
是一个同步过程。当您在中执行例程时,您可以控制该主线程上的任务。建议的重构方法是简单地执行任务,因为您将任务卸载给和执行者(因此是一个新线程)

如其他注释所述,executor中的线程是非守护进程创建的。这意味着当对应用程序调用关闭时,它将等待线程退出,然后再完成最终关闭

所以要处理这个问题,你可以做几件事来处理你的执行者的彻底关闭。要做的第一件事是做另一篇文章中提到的事情,你需要找到你的终止条件

第二,您需要评估您的流程,并查看您是否正在执行任何不能在流程中间停止的关键操作(例如写入磁盘上的文件)

如果您没有做任何重要的事情(这是这里的重要部分),您可以传递一个
ThreadFactory
,它返回一个守护进程线程。这将使JVM终止进程,无论进程是否完成。工厂看起来是这样的(我还为它添加了名称,因为给线程命名总是很好的,它使调试更容易)

处理它的另一种方法是保留
服务.scheduleAtFixedRate
返回的
ScheduledFuture
。使用此对象,您可以在认为合适时取消任务,并在适当的时候关闭执行器

private void test() {
        ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();

        ScheduledFuture<?> future =  exec.scheduleAtFixedRate(new Task(), 0, 5, TimeUnit.MINUTES);

        //if you want to cancel the task
        //boolean is if you want to interrupt while running
        future.cancel(true);
    }
private void test(){
ScheduledExecutorService exec=执行者。newSingleThreadScheduledExecutor();
ScheduledFuture future=exec.scheduleAtFixedRate(newtask(),0,5,TimeUnit.MINUTES);
//如果你想取消任务
//布尔值表示在运行时是否要中断
future.cancel(true);
}
您仍然需要关闭执行器以保持干净,但此时应该没有任何运行


如果由于任何原因无法使用
Future
对象,则需要使用shutdown和shutdownNow来关闭应用程序。如果您正在做一些关键的事情,您需要在任务中使用一个关机挂钩或异步调用,以便正确地清理它正在做的事情并优雅地退出。

在使用Shutdownow()之后,您仍然需要等待Termination()。“从外部杀死”我认为对于JVM来说是未定义的行为。我怀疑是否指定了关闭钩子是否运行。对,非守护进程状态是故意的,否则执行器计划的任务在程序退出之前最多运行一次。我希望终止条件保持不变-即仅通过“从外部杀死”(这不是最好的主意,但…)
private void test() {
        ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();

        ScheduledFuture<?> future =  exec.scheduleAtFixedRate(new Task(), 0, 5, TimeUnit.MINUTES);

        //if you want to cancel the task
        //boolean is if you want to interrupt while running
        future.cancel(true);
    }