难以理解关闭挂钩何时执行以及如何终止 ExecutorService
Difficulty to understand when is shutdown hook executed and how to terminate an ExecutorService
我正在尝试 运行 一个基于 scheduleAtFixedRate
ExecutorService
的程序。
我使用此服务来替换虚拟 while
循环。
全局的想法是首先让一个ExecutorService
(调度器)执行一个Runnable
(运行nable)。然后,出于某种原因,运行nable 可能会或可能不会在另一个 ExecutorService
(tasker)上安排任务。
现在的问题是:
- 我应该如何在停止程序时停止所有这些线程?
我已经尝试了一些,但找不到合适的解决方案。
我尝试了两件事:DaemonThread
、ShutdownHook
守护线程不是我想要的,我希望调度程序保持 运行ning 直到我停止程序。
乍一看,ShutdownHook 不是一个好的解决方案,但经过一些研究后它似乎可行。
- 就是不明白为什么.
问题是,如果我使用命令 mvn exec:java -Dexec.mainClass="com.goodbook.App"
执行代码并使用 Ctrl+C
停止它,ShutdownHook 会 运行ned,但如果我 [=81] 它不会执行=] 我的 IDE (VScode) 中的 java 代码并使用调试器工具停止它。
所以还有两个问题:
- 为什么
ShutdownHook
执行了?
- 为什么 运行 命令行和 运行 IDE
之间的行为不同
首先我会给你两段代码:应用程序和任务class。
App.java
package com.goodbook;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class ShutdownHook {
private static final Logger logger = LoggerFactory.getLogger(ShutdownHook.class);
public void attachShutDownHook(ScheduledExecutorService scheduler, ScheduledExecutorService tasker) {
logger.info("attaching shutdown hook");
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
scheduler.shutdown();
tasker.shutdown();
logger.info("shutdown hook runned");
logger.info("scheduler is down : "+scheduler.isShutdown());
logger.info("tasker is down : "+tasker.isShutdown());
}
});
}
}
public class App
{
private static final Logger logger = LoggerFactory.getLogger(App.class);
public static void main( String[] args )
{
logger.info("starting program");
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(0);
ScheduledExecutorService tasker = Executors.newScheduledThreadPool(0);
ShutdownHook shutdown = new ShutdownHook();
shutdown.attachShutDownHook(scheduler, tasker);
Task t = new Task("task1");
Runnable runnable = () -> {
logger.info("running Tasker 1");
if(!t.isPlanified()){
logger.info("unplanified task found "+t.getName());
logger.info("planning...");
tasker.schedule(t, 1000, TimeUnit.MILLISECONDS);
t.setPlanified(true);
}
};
logger.info("scheduling tasks");
scheduler.scheduleAtFixedRate(runnable, 0, 1000, TimeUnit.MILLISECONDS);
}
}
Task.java
package com.goodbook;
import java.util.concurrent.atomic.AtomicBoolean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Task implements Runnable {
private static final Logger logger = LoggerFactory.getLogger(App.class);
String name;
AtomicBoolean planified = new AtomicBoolean(false);
public Task(String name){
this.name = name;
}
@Override
public void run(){
logger.info(name+" run");
planified.set(false);
}
public Boolean isPlanified(){
return planified.get();
}
public void setPlanified(Boolean b){
planified.set(b);
}
public String getName(){
return name;
}
}
在 运行 VScode 的情况下日志文件结束并使用调试器工具停止执行:
INFO 2019-04-06 16:35:54,121 [pool-1-thread-1] com.goodbook.App - planning...
INFO 2019-04-06 16:35:55,121 [pool-1-thread-1] com.goodbook.App - Running Tasker 1
INFO 2019-04-06 16:35:55,121 [pool-2-thread-4] com.goodbook.TestTask - task1 run
INFO 2019-04-06 16:35:56,121 [pool-1-thread-1] com.goodbook.App - Running Tasker 1
INFO 2019-04-06 16:35:56,121 [pool-1-thread-1] com.goodbook.App - Unplanified task found task1
INFO 2019-04-06 16:35:56,121 [pool-1-thread-1] com.goodbook.App - planning...
在 运行 mvn exec:java -Dexec.mainClass="com.goodbook.App"
的情况下日志文件结束并停止执行 Ctrl+C
:
INFO 2019-04-06 16:59:09,686 [pool-1-thread-1] com.goodbook.App - running Tasker 1
INFO 2019-04-06 16:59:09,688 [pool-2-thread-1] com.goodbook.Task - task1 run
INFO 2019-04-06 16:59:10,686 [pool-1-thread-1] com.goodbook.App - running Tasker 1
INFO 2019-04-06 16:59:10,686 [pool-1-thread-1] com.goodbook.App - unplanified task found task1
INFO 2019-04-06 16:59:10,687 [pool-1-thread-1] com.goodbook.App - planning...
INFO 2019-04-06 16:59:11,686 [pool-1-thread-1] com.goodbook.App - running Tasker 1
INFO 2019-04-06 16:59:11,687 [pool-2-thread-2] com.goodbook.Task - task1 run
INFO 2019-04-06 16:59:12,641 [Thread-1] com.goodbook.ShutdownHook - shutdown hook runned
INFO 2019-04-06 16:59:12,642 [Thread-1] com.goodbook.ShutdownHook - scheduler is down : true
INFO 2019-04-06 16:59:12,642 [Thread-1] com.goodbook.ShutdownHook - tasker is down : true
我真的需要帮助解决这些问题,我不是 java 开发人员 :s
关闭挂钩在 JVM is shutting down; see the class JavaDoc at Runtime.addShutdownHook
时调用。但是从调试器中终止 运行 是 而不是 关机。
正常退出
当在命令行上按下最后一个非 daemon thread exits, or System.exit()
is called via code, or CTRL-C 时,(或向程序发送终止信号,例如使用任务管理器),然后执行关闭挂钩,一旦它们完成后,JVM 将关闭。
退出调试器
如果您在调试期间终止程序并按 "stop",IDE 会立即中止您的应用程序。没有机会 运行 关闭挂钩。这通常是正确的做法;调试时,您通常希望 just kill 无论 运行ning.
调试时优雅退出
如果您想要在调试时 运行 关闭挂钩的方法,有多种方法;一个是调用 System.exit()
via code; that will trigger the hooks. In this 相关问题,他们在调用 system.exit 之前等待按键事件。
关于
how am I supposed to stop all these Threads when I stop the program ?
同样,您可以调用 System.exit()
,这样即使非守护线程正在执行(这不是很干净),程序也会终止;但更好的方法是给每个线程一个正常终止的机会 - 例如。如果线程正在 运行 无限循环,它可以定期检查(易失性)shutdown
变量,并在设置变量时 return (通过代码)。 (shutdown
变量本身应该由关闭挂钩设置)
我正在尝试 运行 一个基于 scheduleAtFixedRate
ExecutorService
的程序。
我使用此服务来替换虚拟 while
循环。
全局的想法是首先让一个ExecutorService
(调度器)执行一个Runnable
(运行nable)。然后,出于某种原因,运行nable 可能会或可能不会在另一个 ExecutorService
(tasker)上安排任务。
现在的问题是:
- 我应该如何在停止程序时停止所有这些线程?
我已经尝试了一些,但找不到合适的解决方案。
我尝试了两件事:DaemonThread
、ShutdownHook
守护线程不是我想要的,我希望调度程序保持 运行ning 直到我停止程序。
乍一看,ShutdownHook 不是一个好的解决方案,但经过一些研究后它似乎可行。
- 就是不明白为什么.
问题是,如果我使用命令 mvn exec:java -Dexec.mainClass="com.goodbook.App"
执行代码并使用 Ctrl+C
停止它,ShutdownHook 会 运行ned,但如果我 [=81] 它不会执行=] 我的 IDE (VScode) 中的 java 代码并使用调试器工具停止它。
所以还有两个问题:
- 为什么
ShutdownHook
执行了? - 为什么 运行 命令行和 运行 IDE 之间的行为不同
首先我会给你两段代码:应用程序和任务class。
App.java
package com.goodbook;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class ShutdownHook {
private static final Logger logger = LoggerFactory.getLogger(ShutdownHook.class);
public void attachShutDownHook(ScheduledExecutorService scheduler, ScheduledExecutorService tasker) {
logger.info("attaching shutdown hook");
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
scheduler.shutdown();
tasker.shutdown();
logger.info("shutdown hook runned");
logger.info("scheduler is down : "+scheduler.isShutdown());
logger.info("tasker is down : "+tasker.isShutdown());
}
});
}
}
public class App
{
private static final Logger logger = LoggerFactory.getLogger(App.class);
public static void main( String[] args )
{
logger.info("starting program");
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(0);
ScheduledExecutorService tasker = Executors.newScheduledThreadPool(0);
ShutdownHook shutdown = new ShutdownHook();
shutdown.attachShutDownHook(scheduler, tasker);
Task t = new Task("task1");
Runnable runnable = () -> {
logger.info("running Tasker 1");
if(!t.isPlanified()){
logger.info("unplanified task found "+t.getName());
logger.info("planning...");
tasker.schedule(t, 1000, TimeUnit.MILLISECONDS);
t.setPlanified(true);
}
};
logger.info("scheduling tasks");
scheduler.scheduleAtFixedRate(runnable, 0, 1000, TimeUnit.MILLISECONDS);
}
}
Task.java
package com.goodbook;
import java.util.concurrent.atomic.AtomicBoolean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Task implements Runnable {
private static final Logger logger = LoggerFactory.getLogger(App.class);
String name;
AtomicBoolean planified = new AtomicBoolean(false);
public Task(String name){
this.name = name;
}
@Override
public void run(){
logger.info(name+" run");
planified.set(false);
}
public Boolean isPlanified(){
return planified.get();
}
public void setPlanified(Boolean b){
planified.set(b);
}
public String getName(){
return name;
}
}
在 运行 VScode 的情况下日志文件结束并使用调试器工具停止执行:
INFO 2019-04-06 16:35:54,121 [pool-1-thread-1] com.goodbook.App - planning...
INFO 2019-04-06 16:35:55,121 [pool-1-thread-1] com.goodbook.App - Running Tasker 1
INFO 2019-04-06 16:35:55,121 [pool-2-thread-4] com.goodbook.TestTask - task1 run
INFO 2019-04-06 16:35:56,121 [pool-1-thread-1] com.goodbook.App - Running Tasker 1
INFO 2019-04-06 16:35:56,121 [pool-1-thread-1] com.goodbook.App - Unplanified task found task1
INFO 2019-04-06 16:35:56,121 [pool-1-thread-1] com.goodbook.App - planning...
在 运行 mvn exec:java -Dexec.mainClass="com.goodbook.App"
的情况下日志文件结束并停止执行 Ctrl+C
:
INFO 2019-04-06 16:59:09,686 [pool-1-thread-1] com.goodbook.App - running Tasker 1
INFO 2019-04-06 16:59:09,688 [pool-2-thread-1] com.goodbook.Task - task1 run
INFO 2019-04-06 16:59:10,686 [pool-1-thread-1] com.goodbook.App - running Tasker 1
INFO 2019-04-06 16:59:10,686 [pool-1-thread-1] com.goodbook.App - unplanified task found task1
INFO 2019-04-06 16:59:10,687 [pool-1-thread-1] com.goodbook.App - planning...
INFO 2019-04-06 16:59:11,686 [pool-1-thread-1] com.goodbook.App - running Tasker 1
INFO 2019-04-06 16:59:11,687 [pool-2-thread-2] com.goodbook.Task - task1 run
INFO 2019-04-06 16:59:12,641 [Thread-1] com.goodbook.ShutdownHook - shutdown hook runned
INFO 2019-04-06 16:59:12,642 [Thread-1] com.goodbook.ShutdownHook - scheduler is down : true
INFO 2019-04-06 16:59:12,642 [Thread-1] com.goodbook.ShutdownHook - tasker is down : true
我真的需要帮助解决这些问题,我不是 java 开发人员 :s
关闭挂钩在 JVM is shutting down; see the class JavaDoc at Runtime.addShutdownHook
时调用。但是从调试器中终止 运行 是 而不是 关机。
正常退出
当在命令行上按下最后一个非 daemon thread exits, or System.exit()
is called via code, or CTRL-C 时,(或向程序发送终止信号,例如使用任务管理器),然后执行关闭挂钩,一旦它们完成后,JVM 将关闭。
退出调试器
如果您在调试期间终止程序并按 "stop",IDE 会立即中止您的应用程序。没有机会 运行 关闭挂钩。这通常是正确的做法;调试时,您通常希望 just kill 无论 运行ning.
调试时优雅退出
如果您想要在调试时 运行 关闭挂钩的方法,有多种方法;一个是调用 System.exit()
via code; that will trigger the hooks. In this 相关问题,他们在调用 system.exit 之前等待按键事件。
关于
how am I supposed to stop all these Threads when I stop the program ?
同样,您可以调用 System.exit()
,这样即使非守护线程正在执行(这不是很干净),程序也会终止;但更好的方法是给每个线程一个正常终止的机会 - 例如。如果线程正在 运行 无限循环,它可以定期检查(易失性)shutdown
变量,并在设置变量时 return (通过代码)。 (shutdown
变量本身应该由关闭挂钩设置)