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