Java ShutDown Hook介绍和使用

这篇具有很好参考价值的文章主要介绍了Java ShutDown Hook介绍和使用。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

概述:

之前有了解过Java的ShutDown Hook机制,但是因为没有使用场景也没有深入学习,最近刚好又看到ShutDown Hook的一些东西,想着学习总结一下,做下学习记录。Java的Shutdown Hook是一种机制,允许开发者在Java虚拟机(JVM)即将关闭之前执行一些清理或终止操作。Shutdown Hook提供了一个钩子,允许开发者在JVM关闭时捕获到关闭事件并执行相应的逻辑。以下是一些使用场景:

  1. 资源释放和清理:当应用程序结束或JVM关闭时,可以使用Shutdown Hook来释放和清理打开的文件、网络连接、数据库连接等资源。这确保资源在程序终止之前得到适当的关闭,避免资源泄露和数据丢失。
  2. 日志记录和统计:Shutdown Hook可以用于记录应用程序的关键统计信息或生成最终的日志报告。通过在JVM关闭前执行这些操作,可以捕获应用程序在运行期间的关键数据,并生成相应的日志记录。
  3. 缓存刷新:如果应用程序使用了缓存机制,可以在JVM关闭前使用Shutdown Hook来刷新缓存,将缓存中的数据写回到持久化存储或其他目标中,确保数据的持久化和一致性。
  4. 任务终止和状态保存:在某些情况下,可能需要在应用程序终止时保存任务的当前状态,以便在下次启动时恢复。通过在Shutdown Hook中执行任务的状态保存操作,可以将任务的状态保存到持久化存储中,并在下次启动时进行恢复。
  5. 线程管理:Shutdown Hook还可以用于管理和终止应用程序中的线程。在JVM关闭前,可以使用Shutdown Hook发送终止信号给正在运行的线程,以确保它们在终止之前完成当前任务并进行清理操作。

上面说的这些使用场景,我都没用到过,大家可以先了解一下对ShutDownHook有一个简单的认识。

分析:

下面我们从源码上去看一下,ShutDown Hook的方法和原理。

跟它相关的主要有两个类ApplicationShutdownHooks和Runtime

class ApplicationShutdownHooks {
    /* The set of registered hooks */
    private static IdentityHashMap<Thread, Thread> hooks;
    static {
        try {
            Shutdown.add(1 /* shutdown hook invocation order */,
                false /* not registered if shutdown in progress */,
                new Runnable() {
                    public void run() {
                        runHooks();
                    }
                }
            );
            hooks = new IdentityHashMap<>();
        } catch (IllegalStateException e) {
            // application shutdown hooks cannot be added if
            // shutdown is in progress.
            hooks = null;
        }
    }


    private ApplicationShutdownHooks() {}

    /* Add a new shutdown hook.  Checks the shutdown state and the hook itself,
     * but does not do any security checks.
     */
    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);
    }

    /* Remove a previously-registered hook.  Like the add method, this method
     * does not do any security checks.
     */
    static synchronized boolean remove(Thread hook) {
        if(hooks == null)
            throw new IllegalStateException("Shutdown in progress");

        if (hook == null)
            throw new NullPointerException();

        return hooks.remove(hook) != null;
    }

    /* Iterates over all application hooks creating a new thread for each
     * to run in. Hooks are run concurrently and this method waits for
     * them to finish.
     */
    static void runHooks() {
        Collection<Thread> threads;
        synchronized(ApplicationShutdownHooks.class) {
            threads = hooks.keySet();
            hooks = null;
        }

        for (Thread hook : threads) {
            hook.start();
        }
        for (Thread hook : threads) {
            try {
                hook.join();
            } catch (InterruptedException x) { }
        }
    }
}
 -- Runtime.class里面的方法
 public void addShutdownHook(Thread hook) {
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(new RuntimePermission("shutdownHooks"));
        }
        ApplicationShutdownHooks.add(hook);
    }

    public boolean removeShutdownHook(Thread hook) {
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(new RuntimePermission("shutdownHooks"));
        }
        return ApplicationShutdownHooks.remove(hook);
    }


    public void halt(int status) {
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkExit(status);
        }
        Shutdown.halt(status);
    }

结合这两个类的源码,我们可以看到添加Hook其实是往ApplicationShutdownHooks的静态Map里面放入新的线程,但是这些线程只是创建后被保存了起来,只有当程序退出时,runHooks被执行,每一个带有Hook任务的线程才的start()方法才被执行,也因为Hook之间是相互独立的线程,所以它们之间执行是没有顺序的,而且因为主线程调用了每个Hook的线程的join方法,所以主线程会等待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");

1.ApplicationShutdownHooks已经在调用Hook时,hooks会置为null,不能在添加hook

2.Hook的Thread不能是已经在运行状态的线程

3.因为储存的Hook是根据线程是否相同来判断的,所以同样的Hook无法被添加

不适用的情况:

1.因为ShutDown Hook只能处理正常退出的情况,kill -9这种是无法处理的

2Shutdown.halt和kill -9一样都是强制退出,不会给Hook执行的机会

使用:

下面放了一些简单的测试ShutDown的小例子,github地址:

 @Test
    public void test1() {
        // 测试正常退出的情况
        Runtime.getRuntime().addShutdownHook(
                new Thread(
                        () -> {
                            System.out.println("hook1 执行了");
                        })
        );
    }
    
    输出:hook1 执行了
 @Test
    public void test2() {
        // 测试Hook执行顺序是否真的无序
        Runtime.getRuntime().addShutdownHook(
                new Thread(
                        () -> {
                            System.out.println("hook1 执行了");
                        })
        );

        Runtime.getRuntime().addShutdownHook(
                new Thread(
                        () -> {
                            System.out.println("hook2 执行了");
                        })
        );
    }
    
    输出:输出结果hook1和hook2会随机打印,没有固定顺序
 @Test
    public void test3() {
        // 测试kill -9 会执行Hook吗
        Runtime.getRuntime().addShutdownHook(
                new Thread(
                        () -> {
                            System.out.println("hook1 执行了");
                        })
        );

        while(true) {

        }

    }
    
    输出:
    @Test
    public void test4() {
        // 测试oom时 会执行Hook吗
        Runtime.getRuntime().addShutdownHook(
                new Thread(
                        () -> {
                            System.out.println("hook1 执行了");
                        })
        );

   
        List<Object> list = Lists.newArrayList();
        while(true) {
           list.add(new ShutDownHookTest());
        }
    }
    
    输出:
     	 java.lang.OutOfMemoryError: GC overhead limit exceeded

         at com.scott.java.task.shutdown.hook.ShutDownHookTest.test4(ShutDownHookTest.java:74)
         。。。省略不重要的日志

         hook1 执行了
 @Test
    public void test5() {
        // 测试移除Hook后,会执行Hook吗
        Thread thread = new Thread(() -> {
            System.out.println("hook1 执行了");
        });

        Runtime.getRuntime().addShutdownHook(thread);
        Runtime.getRuntime().removeShutdownHook(thread);
    }
    
    输出:
    @Test
    public void test6() {
        // 测试执行halt方法后,会执行Hook吗
        Thread thread = new Thread(() -> {
            System.out.println("hook1 执行了");
        });

        Runtime.getRuntime().addShutdownHook(thread);
        Runtime.getRuntime().halt(111);
    }
    输出:
 @Test
    public void test7() {
        // 测试已经执行Hook时,还能添加新的hook吗
        Thread thread = new Thread(() -> {
            System.out.println("hook1 执行了");
            Run
        });

        Runtime.getRuntime().addShutdownHook(thread);
        Runtime.getRuntime().halt(111);
    }
    输出:
    hook1 执行了
	Exception in thread "Thread-0" java.lang.IllegalStateException: Shutdown in progress
  @Test
    public void test8() {
        // 测试重复注册后,会执行Hook吗
        Thread thread = new Thread(() -> {
            System.out.println("hook1 执行了");
        });

        Runtime.getRuntime().addShutdownHook(thread);
        Runtime.getRuntime().addShutdownHook(thread);
    }
    
    输出:java.lang.IllegalArgumentException: Hook previously registered
  @Test
    public void test9() {
        // 测试重复注册后,会执行Hook吗
        Thread thread = new Thread(() -> {
            System.out.println("hook1 执行了");
        });

        thread.start();
        Runtime.getRuntime().addShutdownHook(thread);
    }
    
    输出:
        hook1 执行了

    	java.lang.IllegalArgumentException: Hook already running

总结

1.ShutDown的使用还是比较简单,网上也有分析Spring和Dubbo等开源框架的使用例子,基本上都是用于销毁处理资源释放的问题

2.稍微要注意的就是一些特殊情况,比如hook执行是无序的,不能重复添加相同的hook,已经执行的hook不能再创建新的hook等

3.平时基本没用到过ShutDown Hook,自己想到一个比较有用的场景就是Jvm挂了,在Hook里面给监控程序发通知发邮件之类的,让技术人员来处理

参考资料

1.oracle官网资料

2.Java Shutdown Hook 场景使用和源码分析

3.Adding Shutdown Hooks for JVM Applications文章来源地址https://www.toymoban.com/news/detail-490247.html

到了这里,关于Java ShutDown Hook介绍和使用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • “深入剖析JVM内部机制:了解Java虚拟机的工作原理“

    标题:深入剖析JVM内部机制:了解Java虚拟机的工作原理 摘要:本文将深入剖析JVM内部机制,详细介绍Java虚拟机的工作原理。我们将探讨JVM的组成部分、类加载过程、内存管理、垃圾回收以及即时编译等关键概念。此外,还将提供示例代码来帮助读者更好地理解JVM的内部机制

    2024年02月11日
    浏览(44)
  • 『网络安全科普』Windows安全之HOOK技术机制

    如你所知,Windows系统是建立在事件驱动的机制上的,而每一个事件就是一个消息,每个运行中的程序,也就是所谓的进程,都维护着一个或多个消息队列(消息队列的个数取决于进程内包含的线程的个数)。 网络安全重磅福利:入门进阶全套282G学习资源包免费分享! typed

    2024年02月03日
    浏览(40)
  • 一文了解Copilot插件使用介绍

    在软件开发的不断演进中,AI技术正逐渐渗透到我们的日常工作中。GitHub Copilot作为一个基于OpenAI Codex的插件,为程序员提供了全新的编程体验。本文将深入探讨Copilot插件的使用方法、优势特性以及如何充分发挥其潜力,助你更高效、愉悦地进行编码。 1. Copilot简介: GitHub

    2024年02月02日
    浏览(45)
  • 项目中使用缓存报Redisson is shutdown排查

    上周运维将项目的测试环境从k8s中迁出来后,测试发现储能网关一直在上报数据,但是并没有映射到对应的设备上,排查时发现MQ在正常消费,并没有消息挤压,而且日志也没有报错信息,当时就很纳闷,觉得不可思议,通过过滤日志,发现消费了网关的上报的消息,并没有

    2024年01月16日
    浏览(36)
  • 注意力机制详解系列(一):注意力机制概述

    👨‍💻 作者简介: 大数据专业硕士在读,CSDN人工智能领域博客专家,阿里云专家博主,专注大数据与人工智能知识分享。 公众号: GoAI的学习小屋,免费分享书籍、简历、导图等资料,更有交流群分享AI和大数据,加群方式公众号回复“加群”或➡️点击链接。 🎉 专栏推

    2024年01月25日
    浏览(46)
  • JAVA :Shutdown Signal: channel error; protocol method: #method<channel.close>(reply-code=406, reply

    简介: 在项目开发中,有时可能会遇到 “Shutdown Signal: channel error; protocol method: #methodchannel.close(reply-code=406, reply-text=PRECONDITION_FAILED - unknown delivery tag 0, class-id=60, method-id=80)” 这样的报错信息。该报错通常与消息队列相关,可能会出现在使用 RabbitMQ 等消息中间件时。本篇博客

    2024年02月12日
    浏览(38)
  • JVM工作的总体机制概述

    JVM:Java Virtual Machine,翻译过来是Java虚拟机 JRE:Java Runtime Environment,翻译过来是Java运行时环境 JRE=JVM+Java程序运行时所需要的类库 JDK:Java Development Kits,翻译过来是Java开发工具包 JDK=JRE+Java开发辅助工具 Java源程序→编译→字节码文件→放到JVM上运行 总体机制的粗略描述:

    2024年02月13日
    浏览(39)
  • 【深入了解PyTorch】PyTorch生态系统概述

    PyTorch是一个开源的深度学习框架,被广泛应用于研究和工业界的实际应用中。PyTorch生态系统提供了许多有用的工具和库,用于简化和加速深度学习任务的开发过程。本文将介绍PyTorch生态系统中的一些重要组成部分,包括TorchVision、TorchText、TorchAudio等,并讨论如何将它们与

    2024年02月16日
    浏览(34)
  • 【实战】 九、深入React 状态管理与Redux机制(一) —— React17+React Hook+TS4 最佳实践,仿 Jira 企业级项目(十六)

    学习内容来源:React + React Hook + TS 最佳实践-慕课网 相对原教程,我在学习开始时(2023.03)采用的是当前最新版本: 项 版本 react react-dom ^18.2.0 react-router react-router-dom ^6.11.2 antd ^4.24.8 @commitlint/cli @commitlint/config-conventional ^17.4.4 eslint-config-prettier ^8.6.0 husky ^8.0.3 lint-staged ^13.1.2 p

    2024年02月15日
    浏览(41)
  • 【实战】 九、深入React 状态管理与Redux机制(五) —— React17+React Hook+TS4 最佳实践,仿 Jira 企业级项目(二十)

    学习内容来源:React + React Hook + TS 最佳实践-慕课网 相对原教程,我在学习开始时(2023.03)采用的是当前最新版本: 项 版本 react react-dom ^18.2.0 react-router react-router-dom ^6.11.2 antd ^4.24.8 @commitlint/cli @commitlint/config-conventional ^17.4.4 eslint-config-prettier ^8.6.0 husky ^8.0.3 lint-staged ^13.1.2 p

    2024年02月13日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包