如何在计划进程完成后正确终止我的 java 程序?

How to properly kill my java program when a scheduled process is done?

我有一个简单的 java 程序,用于生成元素并在特定时间每隔 X 秒将它们插入数据库中。

生成是用 scheduleAtFixedRate 完成的。只有一个。

我希望我的程序在计划任务结束时完全退出。为此,我在取消任务时使用 System.exit(),但这是正确的做法吗?

这是我当前的代码:

public static void main(String[] args) throws InterruptedException {

        c = generateDbConnection(url, user, password);

        if (c != null) {
            s = generateDbStatement(c);
        } else {
            System.out.println("ERROR");
            return;
        }
        initialTimestamp = new Date();
        TimeUnit.SECONDS.sleep(1);
        generateForAnHour();

    }

    private final static ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

    /**
     * Generator thread handler Uses Statement from main function
     */
    public static void generateForAnHour() {
        final Runnable runner = new Runnable() {
            public void run() {
                String[][] data = new String[numberOfIds][2];
                for (int i = 0; i < numberOfIds; i++) {
                    data[i] = generateDevice();
                    insertDevice(s, data[i][0], data[i][1]);
                }
                quantityOfIds += numberOfIds;
            }
        };

        final ScheduledFuture<?> generatorHandle = scheduler.scheduleAtFixedRate(runner, 0, 5, TimeUnit.SECONDS);
        scheduler.schedule(new Runnable() {
            public void run() {
                generatorHandle.cancel(true);
                System.out.println("Scheduled ID generator terminated.");
                System.exit(0); //TODO Is it really correct way to do it
            }
        }, timeToRun, TimeUnit.SECONDS);
    }

如果程序具有更多功能,我不确定这是否是停止执行程序的正确方法,但我个人认为这是一种不错的方法。 :D

因此,事实证明,ScheduledExecutorService 似乎使用其默认 ThreadFactory 创建了非守护线程,也许我们需要为其提供一个守护线程。

但是,如果我们要调用 ExecutorService#shutdown 或强制 ExecutorService#shutdownNow,它将停止执行这两个任务,从而删除阻止应用程序结束其作业的线程:

private final static ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

public static void main(String[] args) {
    // Some service code here
    generateForAnHour();
}

public static void generateForAnHour() {
    // Some code that does work
    final Runnable runner = () -> System.out.println("Running...");

    final ScheduledFuture<?> generatorHandle = scheduler.scheduleAtFixedRate(runner, 0, 1, TimeUnit.SECONDS);
    // Code that interrupts the worker after a specified time
    scheduler.schedule(scheduler::shutdown, 5, TimeUnit.SECONDS);
}

输出:

Running...
Running...
Running...
Running...
Running...
Running...

Process finished with exit code 0

希望对您有所帮助。 :D

这个答案是 的次要答案,但它有不同的解决问题的方法,所以我认为创建一个单独的答案是足够合适的。

如果你想在未来有更多的任务,我相信这个解决方案更具扩展性并且更"correct thing"。

它为 runnerinterrupter 创建守护线程。我认为为 interrupter 创建一个普通的线程工厂会更好,但我没能让它工作,所以最好坚持我的第一个答案...

这里generateForAnHourreturn是一个Future用来等待需要的时间

private final static ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1 , new ThreadFactory() {
    @Override
    public Thread newThread(final Runnable r) {
        Thread t = Executors.defaultThreadFactory().newThread(r);
        t.setDaemon(true);
        return t;
    }
});

public static void main(String[] args) throws InterruptedException, ExecutionException {
    // Some service code here

    generateForAnHour().get();
}

public static ScheduledFuture<Boolean> generateForAnHour() {
    // Some code that does work
    final Runnable runner = () -> System.out.println("Running...");
    final ScheduledFuture<?> generatorHandle = scheduler.scheduleAtFixedRate(runner, 0, 1, TimeUnit.SECONDS);

    // Code that interrupts the worker after a specified time
    return scheduler.schedule(() -> generatorHandle.cancel(false), 5, TimeUnit.SECONDS);
}

如果您不打电话给 Future#get,最好的情况下您只会收到一个 Running... 或根本 none。

如果您决定 return runner 未来,您可能会在 get() 通话中变得令人讨厌 CancellationException

 Exception in thread "main" java.util.concurrent.CancellationException
    at java.base/java.util.concurrent.FutureTask.report(FutureTask.java:121)
    at java.base/java.util.concurrent.FutureTask.get(FutureTask.java:191)
    at com.xobotun.Test.main(Test.java:30)

我会使用 ExecutorService::shutdown 方法作为更稳定和更容易理解的方法。 :D