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 相关文章推荐
分析Netty直接内存原理及应用
Jun 14 Java/Android
总结一下关于在Java8中使用stream流踩过的一些坑
Jun 24 Java/Android
Java常用函数式接口总结
Jun 29 Java/Android
java泛型通配符详解
Jul 25 Java/Android
JAVA API 实用类 String详解
Oct 05 Java/Android
Java设计模式之享元模式示例详解
Mar 03 Java/Android
Spring事务管理下synchronized锁失效问题的解决方法
Mar 31 Java/Android
Java 数据结构七大排序使用分析
Apr 02 Java/Android
详解Alibaba Java诊断工具Arthas查看Dubbo动态代理类
Apr 08 Java/Android
引用计数法和root搜索算法以及JVM中判定对象需要回收的方法
Apr 19 Java/Android
Java界面编程实现界面跳转
Jun 16 Java/Android
Java实现注册登录跳转
Jun 16 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
Laravel 5 学习笔记
2015/03/06 PHP
JavaScript中:表达式和语句的区别[译]
2012/09/17 Javascript
javascript获取web应用根目录的方法
2014/02/12 Javascript
jQuery动画特效实例教程
2014/08/29 Javascript
基于jQuery实现下拉框
2014/11/24 Javascript
Node.js的包详细介绍
2015/01/14 Javascript
js实现鼠标移到链接文字弹出一个提示层的方法
2015/05/11 Javascript
详解javascript遍历方式
2015/11/11 Javascript
JavaScript在网页中画圆的函数arc使用方法
2015/11/13 Javascript
JavaScript实现跑马灯抽奖活动实例代码解析与优化(一)
2016/02/16 Javascript
使用JavaScript脚本判断页面是否在微信中被打开
2016/03/06 Javascript
基于BootStrap Metronic开发框架经验小结【五】Bootstrap File Input文件上传插件的用法详解
2016/05/12 Javascript
JavaScript中解决多浏览器兼容性23个问题的快速解决方法
2016/05/19 Javascript
详解vue 中使用 AJAX获取数据的方法
2017/01/18 Javascript
js实现简单的网页换肤效果
2017/01/18 Javascript
AngularJs每天学习之总体介绍
2017/08/07 Javascript
不得不看之JavaScript构造函数及new运算符
2017/08/21 Javascript
JavaScript实现元素滚动条到达一定位置循环追加内容
2017/12/28 Javascript
vue2.0 better-scroll 实现移动端滑动的示例代码
2018/01/25 Javascript
Vue.js 2.x之组件的定义和注册图文详解
2018/06/19 Javascript
nodejs 十六进制字符串型数据与btye型数据相互转换
2018/07/30 NodeJs
jQuery 常用特效实例小结【显示与隐藏、淡入淡出、滑动、动画等】
2020/05/19 jQuery
JavaScript实现烟花绽放动画效果
2020/08/04 Javascript
python的几种开发工具介绍
2007/03/07 Python
通过代码实例展示Python中列表生成式的用法
2015/03/31 Python
利用信号如何监控Django模型对象字段值的变化详解
2017/11/27 Python
Python requests库用法实例详解
2018/08/14 Python
CSS3之2D与3D变换的实现方法
2019/01/28 HTML / CSS
HTML5本地存储之Database Storage应用介绍
2013/01/06 HTML / CSS
Wedgwood英国官方网站:英式精致骨瓷餐具、礼品与生活精品,源于1759年
2019/09/02 全球购物
澳大利亚最早和最古老的巨型游戏专家:Yardgames
2020/02/20 全球购物
校园门卫岗位职责
2013/12/09 职场文书
工商管理专业职业生涯规划
2014/01/01 职场文书
解决numpy数组互换两行及赋值的问题
2021/04/17 Python
MySQL 8.0 驱动与阿里druid版本兼容问题解决
2021/07/01 MySQL
Python MNIST手写体识别详解与试练
2021/11/07 Python