【Java基础】JVM关闭回调函数(ShutdownHook)的应用场景

文章目录

一.ShutdownHook介绍

  • ShutdownHook就是一个简单的 已初始化 但是 未启动 的 线程 。当虚拟机开始关闭时,它将会调用所有已注册ShutdownHook的回调函数,这些回调函数执行是并发的,执行顺序是不确定的。

    • 作用:JVM退出时执行的业务逻辑( 注意:ShutdownHook方法参数必须是Thread的子类,由此得知,ShutdownHook是异步执行的。)
      • 添加:Runtime.getRuntime(). addShutdownHook(Thread var1)
      • 移除:Runtime.getRuntime().removeShutdownHook(this.shutdownHookThread)
  • 需要注意的是,在ShutdownHook里执行的操作应当是不太耗时的。因为在用户注销或者操作系统关机导致的JVM shutdown的例子中,系统只会预留有限的时间给未完成的工作,超时之后还是会强制关闭。

二.ShutdownHook被调用场景

  • 程序正常退出
  • 程序调用 System.exit() 退出
  • 终端使用 Ctrl+C 中断程序
  • 程序抛出异常导致程序退出,例如 OOM,数组越界等异常
  • 系统事件,例如用户注销或关闭系统
  • 使用 Kill pid 命令杀掉进程,注意使用 kill -9 pid 强制杀掉不会触发执行钩子

三.ShutdownHook如何使用

调用java.lang.Runtime这个类的addShutdownHook(Thread hook)方法即可注册一个ShutdownHook,然后在Thread中定义需要在system exit时进行的操作。如下:

java 复制代码
Runtime.getRuntime().addShutdownHook(new Thread(() -> 
    System.out.println("Do something in ShutdownHook")
));

测试例子

  • 首先,注册了一个ShutdownHook。
    • 然后,系统Sleep 3秒,模拟进行某些操作。
      • 然后,调用一个空的List,抛出异常,准备结束程序。
        • 在程序将要结束的时候,执行ShutdownHook中的内容。
groovy 复制代码
public static void main(String[] args)
{
    // register ShutdownHook
    Runtime.getRuntime().addShutdownHook(new Thread(() -> System.out.println("Do something in ShutdownHook")));

    // sleep for some time
    try {
        for (int i=0; i<3; i++) {
            System.out.println("Count: " + i + "...");
            TimeUnit.MILLISECONDS.sleep(1000);
        }
        List nullList = new ArrayList<>();
        System.out.println("Trying to print null list's first element: " + nullList.get(0).toString());
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    System.out.println("Ready to exit.");
    System.exit(0);
}

结果如下:

groovy 复制代码
Count: 0...
Count: 1...
Count: 2...
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
    at java.util.ArrayList.rangeCheck(ArrayList.java:653)
    at java.util.ArrayList.get(ArrayList.java:429)
    at HookTest.main(HookTest.java:18)
Do something in ShutdownHook

Process finished with exit code 1

需要注意的点

  • 当System.exit之后,当ShutdownHook开始执行时,其他的线程还是会继续执行。
  • 应当保证ShutdownHook的线程安全。
    • 在使用多个ShutdownHook时一定要特别小心,保证其调用的服务不会被其他Hook影响。否则会出现当前Hook所依赖的服务被另外一个Hook终止了的情况。

四.ShutdownHook实践

例如,我们程序自定义了一个线程池,用来接收和处理任务。如果程序突然崩溃异常退出,这时线程池的所有任务有可能还未处理完成,如果不处理完程序就直接退出,可能会导致数据丢失,业务异常等重要问题。这时钩子就派上用场了。

groovy 复制代码
 import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
 
public class ShutdownHookDemo {
    // 线程池
    private static ExecutorService executorService = Executors.newFixedThreadPool();
 
    static {
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            System.out.println("开始执行钩子方法...");
            // 关闭线程池
            executorService.shutdown();
            try {
                // 等待秒
                System.out.println(executorService.awaitTermination(, TimeUnit.SECONDS));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("结束执行钩子方法...");
        }));
    }
 
    public static void main(String[] args) throws InterruptedException {
        System.out.println("程序开始启动...");
        // 向线程池添加个任务
        for (int i =; i < 10; i++) {
            Thread.sleep();
            final int finalI = i;
            executorService.execute(() -> {
                try {
                    Thread.sleep();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Task " + finalI + " execute...");
            });
            System.out.println("Task " + finalI + " is in thread pool...");
        }
    }
} 

在命令行窗口中运行程序,在10个任务都提交到线程池之后,任务都还未处理完成之前,使用Ctrl+C中断程序,最终在虚拟机关闭之前,调用了关闭回调函数,关闭线程池,并且等待60秒让所有任务执行完成。

相关推荐
夏幻灵2 小时前
JavaScript this 面试:五种绑定规则、优先级与常见面试陷阱
开发语言·javascript·面试
程序猿编码3 小时前
榨干 RTX5090 算力!Qwen3 专用单卡推理引擎,手写C++/CUDA 算子实现 MTP 推测解码
开发语言·c++·大模型推理·qwen3·多模态推理
bkspiderx4 小时前
Qt 插件机制:动态扩展应用功能的核心框架
开发语言·qt·元数据·qt 插件·qpluginloader
代码什么用4 小时前
Spring基础使用
java·后端·spring
程序员老陆4 小时前
深入理解 C++ thread_local:线程私有存储的正确打开方式
开发语言·c++·程序设计
沐晓时光4 小时前
C语言入门,深入理解指针(3)
c语言·开发语言
Su米苏5 小时前
Spring AI 中MCP 与普通 @Tool的区别
java·人工智能·spring
码云数智-园园5 小时前
unique_ptr 还是 shared_ptr?C++ 智能指针选型与内存泄漏实战分析
java·开发语言
老板一杯拿铁5 小时前
Java 异常体系详解:从字节码原理到最佳实践
java
蜗牛互联网5 小时前
Java 17 HttpClient调用文件转写API的超时与失败回退
java·人工智能·后端