JVM钩子函数的使用场景详解

目录
  • 一、问题引入
    • 背景
    • 原始思路
  • 二、JVM钩子使用场景
    • 正常关闭
    • 异常关闭
    • 强制关闭
  • 三、回归问题

一、问题引入

背景

在编写一个需要持续在后台运行的程序的时候遇到了这样的场景:我的程序在主函数中创建了一个线程池周期性地执行任务,我希望主线程和线程池都持续运行,但如果收到外部的关闭信号时,主线程和线程池也都能同时退出。想到的就是程序结束的时候需要有一个stop()方法去手动关闭线程池,但是怎么控制这个stop()方法在我想要的时候调用,以什么形式去接收外部的关闭信号也成了需要考虑的问题。

原始思路

最开始的尝试是我将程序的运行和停止分别用"start"和"stop"两种状态表示,然后用一个状态文件state去记录当前的状态(程序启动时默认是"start"),如果想要关闭这个正在运行的程序,就去修改状态文件state,将里面内容变为"stop"。同时在主函数中打开这个状态文件,循环监听里面的内容,如果发现变为"stop",就去调用stop()方法执行关闭逻辑。按照这个思路,我写了一个简单的程序在IDEA中测试了一下效果,发现是可行的。但是当我将程序打包,在mac系统上运行jar包进行测试的时候,不知什么原因,程序总是读到state文件刚打开时的内容,不能检测到state文件的变化,无法按我设想的方式进行关闭。因此只能另想办法。

无意间看见JVM钩子函数的介绍,发现这可能正是我想要的,于是赶紧拿来试一试。

二、JVM钩子使用场景

JVM关闭的情况如下图所示分为三类,第一种是正常的关闭,第二种是异常关闭的情况,第三种是强制关闭的情况。

JVM钩子函数对于前两种方式都可以进行优雅的关闭,但是对最后一种强制关闭就不起作用了。

下面我会根据这三种JVM关闭过程进行简单演示。

正常关闭

代码如下:

public class TestJVMHook {
    public static void main(String[] args) {
        Runtime.getRuntime().addShutdownHook(new Thread(()->
                stop()
        ));
        start();
        System.out.println("===程序正常结束===");
    }
    public static void start() {
        System.out.println("===调用start()方法===");
    }

    public static void stop() {
        System.out.println("===调用stop()方法===");
    }
}

运行结果:

===调用start()方法===
===程序正常结束===
===调用stop()方法===

可以看到,在钩子函数中声明了stop()方法,然后程序正常结束后会自动调用钩子函数。

异常关闭

异常关闭分为OOM和RuntimeException两种情况,我用除数为0的运行时异常来演示。

代码如下:

public class TestJVMHook {
    public static void main(String[] args) {
        Runtime.getRuntime().addShutdownHook(new Thread(()->
                stop()
        ));
        start();
        int res = 10/0;
        System.out.println("===程序结束===");
    }
    public static void start() {
        System.out.println("===调用start()方法===");
    }

    public static void stop() {
        System.out.println("===调用stop()方法===");
    }
}

运行结果:

===调用start()方法===
===调用stop()方法===
Exception in thread "main" java.lang.ArithmeticException: / by zero
 at com.example.TestJVMHook.main(TestJVMHook.java:9)

可以看到执行"10/0"时发生运行时异常,并不会正常打印下一行语句,但仍然会自动调用钩子函数中stop()方法。

强制关闭

这里我们启动一个循环程序,然后手动去关闭它。

代码如下:

public class TestJVMHook {
    public static void main(String[] args) throws InterruptedException {
        Runtime.getRuntime().addShutdownHook(new Thread(()->
                stop()
        ));
        int count = 1;
        start();
        while(true){
            System.out.println("循环计数器"+(count++));
            Thread.sleep(5*1000);
        }
    }
    public static void start() {
        System.out.println("===调用start()方法===");
    }

    public static void stop() {
        System.out.println("===调用stop()方法===");
    }
}

启动后查看进程id,然后通过"kill -9 <pid>"强制关闭:

运行结果:

还是上面那段代码,再次启动,采用"kill <pid>"关闭:

发现通过"kill "正常关闭可以有效调用钩子函数,但是"kill -9 "强制关闭则不会调用钩子函数。

三、回归问题

经过一系列测试,验证了JVM钩子函数确实可以实现我想要的资源关闭效果。由于我的程序是一个循环程序,需要手动关闭,因此可以在关闭程序的脚本中通过kill pid的方式进行钩子函数的调用。

到此这篇关于JVM钩子函数使用场景的文章就介绍到这了,更多相关JVM钩子函数使用内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • JVM钩子函数的使用场景详解

    目录 一.问题引入 背景 原始思路 二.JVM钩子使用场景 正常关闭 异常关闭 强制关闭 三.回归问题 一.问题引入 背景 在编写一个需要持续在后台运行的程序的时候遇到了这样的场景:我的程序在主函数中创建了一个线程池周期性地执行任务,我希望主线程和线程池都持续运行,但如果收到外部的关闭信号时,主线程和线程池也都能同时退出.想到的就是程序结束的时候需要有一个stop()方法去手动关闭线程池,但是怎么控制这个stop()方法在我想要的时候调用,以什么形式去接收外部的关闭信号也成了需要考虑的问题. 原

  • React中useLayoutEffect钩子使用场景详解

    目录 简介 useEffect钩子的概述 钩子流程 useLayoutEffect钩子的概述 钩子流程 什么时候使用useLayoutEffect钩子? 总结 简介 不久前,React对其功能组件进行了一次重大更新(在2019年3月的16.8版本中),终于为这些组件提供了一种变得有状态的方法. 钩子的加入不仅意味着功能组件将能够提供自己的状态,而且还能通过引入useEffect钩子来管理自己的生命周期事件. 此外,这次更新还引入了一个全新的useLayoutEffect钩子,根据React文档,

  • SQL开窗函数的具体实现详解

    开窗函数:在开窗函数出现之前存在着很多用 SQL 语句很难解决的问题,很多都要通过复杂的相关子查询或者存储过程来完成.为了解决这些问题,在 2003 年 ISO SQL 标准加入了开窗函数,开窗函数的使用使得这些经典的难题可以被轻松的解决.目前在 MSSQLServer.Oracle.DB2 等主流数据库中都提供了对开窗函数的支持,不过非常遗憾的是 MYSQL 暂时还未对开窗函数给予支持. 开窗函数简介:与聚合函数一样,开窗函数也是对行集组进行聚合计算,但是它不像普通聚合函数那样每组只返回一个值

  • C语言 module_init函数与initcall案例详解

    module_init这个函数对做驱动的人来说肯定很熟悉,这篇文章用来跟一下这个函数的实现. 在include/linux/init.h里面有module_init的定义,自然,因为一个module可以在内核启动时自动加载进内核,也可以由我们手动在需要时加载进内核,基于这种场景,内核使用了MODULE这个宏,见代码: #ifndef MODULE #ifndef __ASSEMBLY__ ... #define __define_initcall(level,fn,id) \ static in

  • Python常用内置函数和关键字使用详解

    目录 常用内置方法 查看所有的内置类和内置方法 标准输入输出 数学 序列 进制数转换 ASCII字符编码转换 其它 常用关键字 常见内置属性 常用内置方法 在Python中有许许多多的内置方法,就是一些Python内置的函数,它们是我们日常中经常可以使用的到的一些基础的工具,可以方便我们的工作. 查看所有的内置类和内置方法 # 方法一 built_list = dir(__builtins__) # 方法二 import builtins built_list = dir(builtins) 其

  • TypeScript声明文件的语法与场景详解

    目录 简介 语法 内容 模块化 模块语法 三斜线指令 reference amd-module 场景 1. 在内部项目中给内部项目写声明文件 2. 给第三方包写声明文件 全局变量的第三方库 修改全局变量的模块的第三方库的声明 修改window ESM和CommonJS UMD 模块插件 总结 简介 声明文件是以.d.ts为后缀的文件,开发者在声明文件中编写类型声明,TypeScript根据声明文件的内容进行类型检查.(注意同目录下最好不要有同名的.ts文件和.d.ts,例如lib.ts和lib.

  • Android 运用@JvmName解决函数签名冲突问题详解

    Kotlin(JVM) 中定义下面这样两个方函数时,编译器会报错 fun foo(value: List<String>) {} fun foo(value: List<Int>) {} Platform declaration clash: The following declarations have the same JVM signature (method(Ljava/util/List;)V): 因为 Java 的泛型编译期擦除,所以 JVM 无法识别签名中泛型的区别,

  • MySQL函数Locate的使用详解

    Locate函数主要的作用是判断一个字符串是否包含另一个字符串,如 Locate(str,sub) > 0,表示sub字符串包含str字符串: Locate(str,sub) = 0,表示sub字符串不包含str字符串. 该函数常常与<![CDATA[]]> 组件一起使用,该组件作用是防止字符串进行转义.比如: select * from stu s where <![CDATA[Locate(#{name},s.name)>0]]> mysql的instr函数有着相似

  • JS JSON.stringify()的5个使用场景详解

    目录 前言 第二个参数replacer 为数组 第二个参数replacer 为函数 第三个参数为 Number 第三个参数为 String toJSON 方法 总结 前言 JSON.stringify() 方法将一个 JavaScript 对象或值转换为 JSON 字符串,如果指定了一个 replacer 函数,则可以选择性地替换值,或者指定的 replacer 是数组,则可选择性地仅包含数组指定的属性. 语法如下: JSON.stringify(value[, replacer [, spac

  • 配置管理和服务发现之Confd和Consul使用场景详解

    目录 Confd和Consul是什么鬼? Confd Consul Confd Consul Confd+Consul 案例1 案例2 Confd和Consul是什么鬼? Confd和Consul都是用于配置管理和服务发现的工具. https://www.consul.io/ https://www.tail-f.com/confd-basic/ Confd Confd是一个轻量级的工具,用于管理分布式系统中的配置文件.它通过将配置文件和模板分离来解决配置管理的挑战.Confd监视由Etcd.Z

随机推荐