Java并发:运行直到终止

时间:2014-08-08 16:04:06

标签: 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) {
    ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
    service.scheduleAtFixedRate(new Task(...), 0, 5, TimeUnit.MINUTES);
}

我对此有一些担忧,但是:首先,程序在调度后“终止”(如在main()中返回),但JVM保持活动,因为{{1 }仍在运行。我认为这是故意的,为了维护旧代码“保持运行直到从外部终止”的行为,但我相信我必须安装一个关闭钩子以确保ScheduledExecutorService正确关闭。实际情况是这样,还是“从外面杀死”也会阻止执行关机钩?

其次,我也对首先关闭钩子感到娇气。如果我有像

这样的代码
ScheduledExecutorService

上述同样“被外界杀害”的问题仍然存在吗?

编辑:是的,原始程序在没有突破循环的情况下循环;它只是意味着从SIGINT(终端中的control-C)终止。这是一个相当不明确的终止条件,但这是我现在唯一的事情......

3 个答案:

答案 0 :(得分:2)

您展示的2种方法之间存在根本区别。 while(true)是一个同步过程。当您在其中执行例程时,您可以控制该主线程上的任务。由于您将任务卸载到执行程序(因此是一个新线程),所以提议的重构方法是asychnoniouyly执行任务。

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

因此,要处理此问题,您可以执行以下操作来处理执行程序的干净关闭。首先要做的是在另一篇文章中提到的内容,你需要找到你的终止条件。

其次,您需要评估您的流程,看看您是否正在做任何不能停止中间过程的关键事件(例如写入磁盘上的文件)。

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

public class DaemonThreadFactory implements ThreadFactory {

    private final String NAME;

    public DaemonThreadFactory() {
        this(null);
    }

    public DaemonThreadFactory(final String name) {
        NAME = name;
    }

    @Override
    public Thread newThread(Runnable r) {
        Thread t = new Thread(r);
        t.setDaemon(true);
        if(NAME != null) {
        t.setName(NAME);
        }
        return t;
    }
}

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

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);
    }

你仍然需要关闭执行程序才能清理,但此时应该没有任何运行

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

答案 1 :(得分:1)

当您的Java进程被终止时,线程将不会收到异常;他们会突然死去。因此,finally方法无济于事。关闭挂钩将运行,但如果您被SIGKILL杀死则不会。但是,关闭钩子将无法做很多事情,因为当时ExecutorService的池线程可能已经死亡。

换句话说,您应该更好地寻找更高级别的方法来要求您的Java程序完成。

请记住,使用无限循环的当前代码与基于ExecutorService的解决方案在杀死信号时的行为方面处于平等地位。

答案 2 :(得分:0)

按照设计,执行程序默认在非守护程序线程中运行。这意味着您需要某种方式告诉执行程序终止,否则您的程序将不会退出。

您需要考虑您希望程序退出的条件。然后,在这种情况下,安排执行人员关闭。