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


Posted in Java/Android onAugust 23, 2021

一、问题引入

背景

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

原始思路

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

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

二、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>"强制关闭:

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

运行结果:

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

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

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

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

三、回归问题

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

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

Java/Android 相关文章推荐
springboot集成flyway自动创表的详细配置
Jun 26 Java/Android
Sleuth+logback 设置traceid 及自定义信息方式
Jul 26 Java/Android
java多态注意项小结
Oct 16 Java/Android
maven依赖的version声明控制方式
Jan 18 Java/Android
详解Spring Security中的HttpBasic登录验证模式
Mar 17 Java/Android
Spring Boot 底层原理基础深度解析
Apr 03 Java/Android
Android Rxjava3 使用场景详解
Apr 07 Java/Android
零基础学java之循环语句的使用
Apr 10 Java/Android
Spring Boot接口定义和全局异常统一处理
Apr 20 Java/Android
Android Studio实现简易进制转换计算器
May 20 Java/Android
利用Java连接Hadoop进行编程
Jun 28 Java/Android
前端与RabbitMQ实时消息推送未读消息小红点实现示例
Jul 23 Java/Android
Java中CyclicBarrier和CountDownLatch的用法与区别
Aug 23 #Java/Android
SpringBoot整合Mybatis Generator自动生成代码
Aug 23 #Java/Android
Java面试题冲刺第十九天--数据库(4)
Java获取e.printStackTrace()打印的信息方式
Aug 07 #Java/Android
Java移除无效括号的方法实现
Aug 07 #Java/Android
简述Java中throw-throws异常抛出
Aug 07 #Java/Android
Java比较两个对象中全部属性值是否相等的方法
Aug 07 #Java/Android
You might like
PHP制作用户注册系统
2015/10/23 PHP
jQuery表单验证插件formValidator(改进版)
2012/02/03 Javascript
js移除事件 js绑定事件实例应用
2012/11/28 Javascript
Javascript 浮点运算的问题分析与解决方法
2013/08/27 Javascript
批量修改标签css样式以input标签为例
2014/07/31 Javascript
Mac OS X 系统下安装和部署Egret引擎开发环境
2014/09/03 Javascript
jQuery中odd选择器的定义和用法
2014/12/23 Javascript
Javascript 拖拽的一些简单的应用(逐行分析代码,让你轻松了拖拽的原理)
2015/01/23 Javascript
jquery中filter方法用法实例分析
2015/02/06 Javascript
JavaScript实现将UPC转换成ISBN的方法
2015/05/26 Javascript
javascript同步服务器时间和同步倒计时小技巧
2015/09/24 Javascript
jQuery使用$获取对象后检查该对象是否存在的实现方法
2016/09/04 Javascript
js省市区级联查询(插件版&amp;无插件版)
2017/03/21 Javascript
AngularJS2中一种button切换效果的实现方法(二)
2017/03/27 Javascript
微信小程序“摇一摇”的实例代码
2017/07/20 Javascript
JS实现登录页密码的显示和隐藏功能
2017/12/06 Javascript
layui表格分页 记录勾选的实例
2019/09/02 Javascript
vue实现短信验证码登录功能(流程详解)
2019/12/10 Javascript
[00:42]《辉夜杯》—职业组预选赛12月3日15点 正式打响
2015/12/03 DOTA
[06:45]2018DOTA2亚洲邀请赛 4.5 SOLO赛 Sccc vs Maybe
2018/04/06 DOTA
python使用reportlab画图示例(含中文汉字)
2013/12/03 Python
Python判断值是否在list或set中的性能对比分析
2016/04/16 Python
tensorflow实现逻辑回归模型
2018/09/08 Python
python实现车牌识别的示例代码
2019/08/05 Python
python3 反射的四种基本方法解析
2019/08/26 Python
关于pytorch中全连接神经网络搭建两种模式详解
2020/01/14 Python
Python用access判断文件是否被占用的实例方法
2020/12/17 Python
基于Pytorch版yolov5的滑块验证码破解思路详解
2021/02/25 Python
HTML4和HTML5之间除了相似以外的10个主要不同
2012/12/13 HTML / CSS
移动端HTML5实现文件上传功能【附代码】
2016/03/25 HTML / CSS
工程师求职简历的自我评价分享
2013/10/10 职场文书
财务会计大学生自我评价
2014/04/09 职场文书
创文明城市标语
2014/06/16 职场文书
商业计划书范文
2019/04/24 职场文书
Java字符串逆序方法详情
2022/03/21 Java/Android
Java实现添加条码或二维码到Word文档
2022/06/01 Java/Android