0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

java小知识-ShutdownHook(优雅关闭)

京东云 来源:京东物流 崔冬冬 作者:京东物流 崔冬冬 2024-12-19 10:36 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

作者:京东物流 崔冬冬

一、先提出一个问题

我们如果在JVM退出的时候做一些事情,比如关闭远程链接,怎么实现呢?

二、ShutdownHook简介

java里有个方法Runtime.getRuntime#addShutdownHook,是否了解呢?

ShutdownHook是什么意思呢,看单词解释“关闭钩子”,addShutdownHook就是添加一个关闭钩子,这个钩子是做什么的呢?能否解决上面的问题?

1、RunTime类

先看一下看源码RunTime#addShutdownHook方法与解释。

1.1 方法解释

核心意思,在Java虚拟机在关闭时会触发一些自己添加的事件。

Registers a new virtual-machine shutdown hook.
The Java virtual machine shuts down in response to two kinds of events:
The program exits normally, when the last non-daemon thread exits or when the exit (equivalently, System.exit) method is invoked, or
The virtual machine is terminated in response to a user interrupt, such as typing ^C, or a system-wide event, such as user logoff or system shutdown.
A shutdown hook is simply an initialized but unstarted thread. When the virtual machine begins its shutdown sequence it will start all registered shutdown hooks in some unspecified order and let them run concurrently. 
When all the hooks have finished it will then halt. Note that daemon threads will continue to run during the shutdown sequence, as will non-daemon threads if shutdown was initiated by invoking the exit method.

1.2 方法源码

  public void addShutdownHook(Thread hook) {
        @SuppressWarnings("removal")
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(new RuntimePermission("shutdownHooks"));
        }
        ApplicationShutdownHooks.add(hook);
    }

方法内部调用了ApplicationShutdownHooks#add, 我们继续往下看。

2、ApplicationShutdownHooks类

2.1 添加钩子


     private static IdentityHashMap< Thread, Thread > hooks;
     static synchronized void add(Thread hook) {
        if(hooks == null)
            throw new IllegalStateException("Shutdown in progress");
        if (hook.isAlive())
            throw new IllegalArgumentException("Hook already running");
        if (hooks.containsKey(hook))
            throw new IllegalArgumentException("Hook previously registered");
        hooks.put(hook, hook);
    }

我们添加了一个钩子,这个钩子是个线程,这个线程怎么执行的呢? 继续看一下此类中的runHooks。

2.2 执行钩子


static void runHooks() {
        Collection< Thread > threads;
        synchronized(ApplicationShutdownHooks.class) {
            threads = hooks.keySet();
            hooks = null;
        }
        for (Thread hook : threads) {
            hook.start();
        }
        for (Thread hook : threads) {
            while (true) {
                try {
                    hook.join();
                    break;
                } catch (InterruptedException ignored) {
                }
            }
        }
    }

执行runHooks的时候,会启动所有的hook线程,什么时候调用runHooks方法的呢?

2.3 执行时机

为什么在系统退出的时候会执行添加的hook呢?我们看一下正常的退出操作System#exit方法。

1) 类调用层级

System->Runtime->Shutdown->ApplicationShutdownHooks

2) 方法调用

系统退出入口:System#exit

步骤 1-->System#exit

步骤 2-->Runtime#exit;

步骤 3--> Shutdown#exit

步骤 4--> Shutdown#runHooks

步骤 5--> ApplicationShutdownHooks#runHooks

步骤 6-->启动添加的hook线程

3) 补充一下

为什么步骤4会调用到步骤5呢?

可以看一下ApplicationShutdownHooks的构造函数,在创建的时候,封装了runHooks方法,放到了Shutdown的钩子集合里。

如此形成闭环,在系统正常退出的时候,最终执行我们添加的hook。

三、举个例子

了解了基本原理,我们看一下怎么使用的

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread() {
            @Override
            public void run() {
                System.out.println("等等我");
            }
        };
        Runtime.getRuntime().addShutdownHook(thread);
        System.out.println("程序关闭"); 
   }
    输出:
    程序关闭
    等等我

可以看到,在JVM退出的时候调用,执行了此线程,我们开发中,哪些场景可以使用呢?

四、应用场景

关闭链接、线程、资源释放、记录执行状态等。

五、风险点

1、长时间等待

如果添加的hook线程长时间执行,我们的退出命令会一直等待,为什么呢?

举个例子,我们在执行的时候sleep一下

  public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000*300);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(new Date()+" 等我5分钟");
            }
        };
        Runtime.getRuntime().addShutdownHook(thread);
        System.out.println(new Date()+" 程序关闭");
    }
输出:
Tue Nov 12 17:37:38 CST 2024 程序关闭
Tue Nov 12 17:42:38 CST 2024 等我5分钟

2、原因

JVM在退出的时候会调用runHooks方法,看一下上面的方法java.lang.ApplicationShutdownHooks#runHooks方法。

关键字 hook.join(); 主线程会等待子线程执行完成。

如果程序一直执行,不能退出怎么办?

3、解决方案

1 ) 写代码时候控制执行逻辑、时长

kill -9 命令 强制退出

六、扩展

1、Runtime.getRuntime#addShutdownHook是面向开发者

ApplicationShutdownHook#add、Shutdown#add我们都不能直接使用。

2、许多中间件框架也利用addShutdownHook来实现资源回收、清理等操作

比如Spring框架中,使用了ShutdownHook注册,我们常用的@PreDestroy在Bean销毁前执行一些操作,也是借助其回调的。

七、总结

1、本文简单介绍了一下ShutdownHook使用、原理、风险点。

2、我们工作中可以自己注册ShutdownHook,主动释放一些资源,降低风险。

3、小知识分享,不足之处欢迎大家指正,关于java里的知识点也欢迎大家讨论分享。

审核编辑 黄宇

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • JAVA
    +关注

    关注

    20

    文章

    3006

    浏览量

    116828
  • JVM
    JVM
    +关注

    关注

    0

    文章

    161

    浏览量

    13085
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    Java并发编程的“基石”——多线程概念初识

    AI 算力调度底层:Java 并发基石与未来技术融合 当我们在屏幕前流畅地与大语言模型对话,或是看着自动驾驶系统瞬间处理海量视觉数据时,往往只会惊叹于 AI 算法的精妙。然而,在这层绚丽的应用外衣
    发表于 04-16 18:50

    Java 开发者指南:全面对比传统 IDE AI 插件与 Cursor、Windsurf 等 AI 原生 IDE

    AI 正在以前所未有的速度重塑 Java 开发工作流。本文将为您深度解析 AI 在 Java 开发中的核心应用场景,并全面对比传统 IDE(如 IntelliJ IDEA、VS Code)的 AI
    的头像 发表于 03-26 14:36 573次阅读
    <b class='flag-5'>Java</b> 开发者指南:全面对比传统 IDE AI 插件与 Cursor、Windsurf 等 AI 原生 IDE

    如何关闭或开启主设备Wi-Fi

    有用户反馈想要关闭或开启主FTTR设备的Wi-Fi,您可以登录主FTTR的web界面进行操作,具体操作如下:
    的头像 发表于 03-12 09:59 368次阅读
    如何<b class='flag-5'>关闭</b>或开启主设备Wi-Fi

    开发知识库测试添加知识

    文档类型的知识要等待数据校验完成后才能上架 可以点击知识名称查看知识详情 等待后端处理完成可以点击知识列表的上架 在智能体中知识库的位置点
    发表于 03-06 15:07

    freertos关闭任务调度的方法

    #include \"FreeRTOS.h\" #include \"task.h\" /* 关闭任务调度 */ void
    发表于 11-17 06:47

    电能质量在线监测装置的报警功能可以关闭吗?

    电能质量在线监测装置的报警功能 能否关闭及如何关闭,取决于装置的设计、功能等级和具体配置 。以下是详细分析: 一、核心结论:报警功能可部分关闭,但安全相关报警通常不可禁用 多数装置支持选择性
    的头像 发表于 11-05 13:38 590次阅读

    Arm Neoverse CPU上大代码量Java应用的性能测试

    Java 是互联网领域广泛使用的编程语言。Java 应用的一些特性使其性能表现与提前编译的原生应用(例如 C 程序)大相径庭。由于 Java 字节码无法直接在 CPU 上执行,因此通常运行时在
    的头像 发表于 11-05 11:25 934次阅读
    Arm Neoverse CPU上大代码量<b class='flag-5'>Java</b>应用的性能测试

    交易关闭自动处理接口

    ​  在电商、支付系统或任何交易密集型应用中,交易关闭(如用户取消订单、支付超时或系统异常)是常见事件。手动处理这些事件效率低下且易出错,因此开发一个自动处理接口至关重要。本文将从需求分析、工作原理
    的头像 发表于 10-17 14:25 608次阅读
    交易<b class='flag-5'>关闭</b>自动处理接口

    Java效率提升指南:5个Java工具选型建议及Perforce JRebel和XRebel介绍

    企业级Java环境越来越复杂,真正的破局点,可能不在“人”,而在于“工具”。5个实用建议,帮你理清Java工具的选型思路。
    的头像 发表于 09-11 13:59 1889次阅读
    <b class='flag-5'>Java</b>效率提升指南:5个<b class='flag-5'>Java</b>工具选型建议及Perforce JRebel和XRebel介绍

    Java 在物联网与嵌入式系统中的应用前景与挑战

    引言 随着物联网与嵌入式技术的快速发展,设备端的软件开发需求不断增加。传统上,嵌入式设备多使用 C、C++ 进行开发,但近年来,Java 逐渐成为物联网领域的重要角色。凭借其跨平台特性、丰富
    的头像 发表于 09-04 14:49 1143次阅读

    Perforce JRebel 简介:即时加载代码变更,加速Java应用开发

    Perforce JRebel 专为Java开发提速而生!支持跳过构建与重新部署,实时加载代码变更,支持100+框架,无缝集成主流IDE与应用服务器。
    的头像 发表于 08-14 14:35 981次阅读
    Perforce JRebel 简介:即时加载代码变更,加速<b class='flag-5'>Java</b>应用开发

    A21:分立元件知识与应用专题--电感知识及应用案例

    分立元件知识与应用专题--电感知识及应用案例
    的头像 发表于 07-15 19:24 524次阅读
    A21:分立元件<b class='flag-5'>知识</b>与应用专题--电感<b class='flag-5'>知识</b>及应用案例

    A21:分立元件知识与应用专题--电容知识及应用案例

    分立元件知识与应用专题--电容知识及应用案例
    的头像 发表于 07-15 19:22 530次阅读
    A21:分立元件<b class='flag-5'>知识</b>与应用专题--电容<b class='flag-5'>知识</b>及应用案例

    EtherCAT运动控制卡应用开发教程之Java

    运动控制卡的Java开发及DLL调用
    的头像 发表于 06-13 14:29 1007次阅读
    EtherCAT运动控制卡应用开发教程之<b class='flag-5'>Java</b>

    Java开发者必备的效率工具——Perforce JRebel是什么?为什么很多Java开发者在用?

    Perforce JRebel是一款Java开发效率工具,旨在帮助java开发人员更快地编写更好的应用程序。JRebel可即时重新加载对代码的修改,无需重启或重新部署应用程序,就能让开发者即时看到代码更改的效果,从而缩短开发、调试和测试周期,大大提升开发效率。
    的头像 发表于 04-27 13:44 975次阅读
    <b class='flag-5'>Java</b>开发者必备的效率工具——Perforce JRebel是什么?为什么很多<b class='flag-5'>Java</b>开发者在用?