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

京东云开发者
• 阅读 17

一、先提出一个问题

我们如果在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 ) 写代码时候控制执行逻辑、时长

2) kill -9 命令 强制退出

六、扩展

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

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

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

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

七、总结

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

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

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

点赞
收藏
评论区
推荐文章
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
冴羽 冴羽
3年前
VuePress 博客之 SEO 优化(四) Open Graph protocol
前言在中,我们使用VuePress搭建了一个博客,最终的效果查看:。本篇讲讲SEO优化中的OpenGraphprotocol。meta标签如果我们打开思否任意一篇文章,比如这篇,查看DOM元素,我们可以在head中找到这样一段meta标签:我们可以发现name都是以og:开头,这是什么意思呢,又是什么作用呢?其实这是
捉虫大师 捉虫大师
4年前
ShutdownHook原理
ShutdownHook介绍在java程序中,很容易在进程结束时添加一个钩子,即ShutdownHook。通常在程序启动时加入以下代码即可javaRuntime.getRuntime().addShutdownHook(newThread()@Overridepublicvoidrun()System.out.prin
Easter79 Easter79
4年前
springboot项目在docker容器中如何优雅关闭
前言什么是优雅关闭在我看来所谓的优雅关闭,就是在系统关闭时,预留一些时间,让你有机会来善后一些事情什么时候需要优雅关闭是否所有项目都需要优雅关闭?那也不一定,毕竟所谓的优雅关闭,另一面就意味这关闭得慢,因此项目的优雅关闭得看项目的核心程度,换言之就是看该项目处理的数据是不是核心数据,其实项目的最终本质,是对数据的处理。
红烧土豆泥 红烧土豆泥
4年前
关于如何优雅的关闭JVM
关于如何优雅的关闭JVM使用的类及其方法:Runtime.addShutdownloadHook(Threadhook)虚拟机关闭以及响应两种事件:程序正常退出:最后一个守护线程退出或者调用exit(相当于System,exit)方法时虚拟机响应用户中断:(键入^C)或者系统范围内的事件(用户注销或者系统关闭)而终止language/
Stella981 Stella981
4年前
HashMap原理学习
概述HashMap对于做Java的小伙伴来说太熟悉了。估计你们每天都在使用它。它为什么叫做HashMap?它的内部是怎么实现的呢?为什么我们使用的时候很多情况都是用String作为它的key呢?带着这些疑问让我们来了解HashMap!HashMap介绍1、介绍HashMap是一个用”KEY”“VALUE”
Stella981 Stella981
4年前
Phper 学 C 兴趣入门
引子为什么php手册里经常说某个函数是二进制安全的?我们平常使用函数的时候也没发现有什么区别呀,那么二进制安全到底是什么意思呢?Php实验<?phpechostrlen("abc");//3echostrlen("abc\0");//4echostrlen("abc\0
Stella981 Stella981
4年前
JVM的关闭钩子
什么是关闭钩子(ShutdownHook)?先看看JavaDoc的说明:!(https://static.oschina.net/uploads/space/2016/1223/170211_Gknj_1434710.png)关闭钩子是指通过Runtime.addShutdownHook注册的但尚未开始的线程。这些钩子可以用于实现服务或
Stella981 Stella981
4年前
JVM ZeroTLAB 是什么意思呢?
ZeroTLAB是JVM的一个布尔型Flag,意思是是否将新创建的TLAB内的所有字节归零。默认:false举例:XX:ZeroTLAB当分配出来TLAB之后,根据ZeroTLAB配置,决定是否将每个字节赋0。在TLAB申请时,由于申请TLAB都发生在对象分配的时候,也就是这块内存会立刻被
京东云开发者 京东云开发者
10个月前
java小知识-ShutdownHook(优雅关闭)
作者:京东物流崔冬冬一、先提出一个问题我们如果在JVM退出的时候做一些事情,比如关闭远程链接,怎么实现呢?二、ShutdownHook简介java里有个方法Runtime.getRuntimeaddShutdownHook,是否了解呢?ShutdownHo