难以理解关闭挂钩何时执行以及如何终止 ExecutorService

Difficulty to understand when is shutdown hook executed and how to terminate an ExecutorService

我正在尝试 运行 一个基于 scheduleAtFixedRate ExecutorService 的程序。 我使用此服务来替换虚拟 while 循环。 全局的想法是首先让一个ExecutorService(调度器)执行一个Runnable(运行nable)。然后,出于某种原因,运行nable 可能会或可能不会在另一个 ExecutorService(tasker)上安排任务。 现在的问题是:

我已经尝试了一些,但找不到合适的解决方案。 我尝试了两件事:DaemonThreadShutdownHook 守护线程不是我想要的,我希望调度程序保持 运行ning 直到我停止程序。 乍一看,ShutdownHook 不是一个好的解决方案,但经过一些研究后它似乎可行。

问题是,如果我使用命令 mvn exec:java -Dexec.mainClass="com.goodbook.App" 执行代码并使用 Ctrl+C 停止它,ShutdownHook 会 运行ned,但如果我 [=81] 它不会执行=] 我的 IDE (VScode) 中的 java 代码并使用调试器工具停止它。 所以还有两个问题:

首先我会给你两段代码:应用程序和任务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 变量本身应该由关闭挂钩设置)