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 的方式进行钩子函数的调用。
JVM钩子函数的使用的更多相关文章
- java 关闭钩子函数的应用
Runtime.getRuntime().addShutdownHook(shutdownHook); 说明:这个方法的意思就是在jvm中增加一个关闭的钩子,当jvm关闭的时候,会执行系统中已经设置的 ...
- Vue2.X的路由管理记录之 钩子函数(切割流水线)
$route可以在子组件任何地方调用,代表当前路由对象,这个属性是只读的,里面的属性是 immutable(不可变) 的,不过你可以 watch(监测变化) 它. 导航和钩子函数: 导航:路由正在发生 ...
- 【Mocha.js 101】钩子函数
前情提要 在上一篇文章<[Mocha.js 101]同步.异步与 Promise>中,我们学会了如何对同步方法.异步回调方法以及 Promise 进行测试. 在本篇文章中,我们将了解到 M ...
- wordpress钩子和钩子函数
ccc,看了很多博客,无法理解,还是自己来写吧. wordpress 在wordpress中有很多钩子,还有很多钩子函数,在什么地方用什么钩子,用什么钩子函数, 需要明白两个问题: 1:什么是钩子,钩 ...
- 使用PreTranslateMessage替代钩子函数处理键盘消息
2002年左右,我所在公司在开发基于H.323的VoIP电话系统(用了以色列一家公司的库,具体名字忘记了). 去电信科技研究院测试系统,同事发现处理键盘消息总有一些莫名其妙的问题,比如延迟或异常. 我 ...
- 10.PHP内核探索:Apache运行与钩子函数
Apache是目前世界上使用最为广泛的一种Web Server,它以跨平台.高效和稳定而闻名.按照去年官方统计的数据,Apache服务器的装机量占该市场60%以上的份额.尤其是在 X(Unix/Lin ...
- 对于数据包的截取,使用linux中的netfilter钩子函数
http://blog.csdn.net/wswifth/article/details/5115358 在师哥的代码(packet.c)中使用的是Linux2.4内核中的一个子系统:netfilte ...
- 黄聪:WordPress动作钩子函数add_action()、do_action()源码解析
WordPress常用两种钩子,过滤钩子和动作钩子.过滤钩子相关函数及源码分析在上篇文章中完成,本篇主要分析动作钩子源码. 然而,在了解了动作钩子的源码后你会发现,动作钩子核心代码竟然跟过滤钩子差不多 ...
- 利用钩子函数来捕捉键盘响应的windows应用程序
一:引言: 你也许一直对金山词霸的屏幕抓词的实现原理感到困惑,你也许希望将你的键盘,鼠标的活动适时的记录下来,甚至你想知道木马在windows操作系统是怎样进行木马dll的加载的…..其实这些都是用到 ...
随机推荐
- mybatis源码简单分析
mybatis入门介绍 /** * 1. 接口式编程 * 原生: Dao =====> DaoImpl * mybatis : Mapper =====> xxxMapper * 2. S ...
- Redis 高级面试题
Redis 持久化机制 Redis 是一个支持持久化的内存数据库,通过持久化机制把内存中的数据同步到硬盘文件来 保证数据持久化.当 Redis 重启后通过把硬盘文件重新加载到内存,就能达到恢复数据的目 ...
- Gitbook配置目录折叠
如果有多个目录,Gitbook在浏览器上打开时,默认所有的目录都会打开,当目录比较多时,全部显示不利于阅读. 可以使用插件配置目录折叠,使得打开浏览器时这些目录默认是关闭的. 在执行gitbook i ...
- centos7 PostgreSQL_12.7安装-TimeScaleDB_2.01插件安装
一.安装psql的yum源 sudo yum install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64 ...
- ScienceDirect内容爬虫
爬虫违法,本贴方法只限于个人对数据的分析使用,其爬虫程序已作相关设置,以减小服务器压力.不适宜长期使用. 一.前期准备 1.使用chrome打开ScienceDirect网站(https://www. ...
- ES6 let const关键字
在es6中,引入了let和const关键字: 1.letES6 新增了let命令,用来声明变量.它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效. (1)在块级作用域里有效(比 ...
- C#中使用jieba.NET、WordCloudSharp制作词云图
目录 词云简介 准备工作 基本算法 算法实现 运行测试 参考资料 词云简介 "词云"由美国西北大学新闻学副教授.新媒体专业主任里奇·戈登(Rich Gordon)于2006年最先使 ...
- vulnhub-DC:4靶机渗透记录
准备工作 在vulnhub官网下载DC:4靶机https://www.vulnhub.com/entry/dc-4,313/ 导入到vmware,设置成NAT模式 打开kali准备进行渗透(ip:19 ...
- Django关闭html转义
我们在views定义的html语句传递到html文件会按照原样式输出,并把我们定义的html标签页输出了,这是因为django模板默认帮我们开起了html转义功能 {{ lp}} <hr> ...
- (Opencv07)绘制边框
(Opencv07)绘制边框 cv2.boundingRect(img) 这个函数可以获得一个图像的最小矩形边框一些信息 cv2.rectangle()可以画出该最小矩形边框 x, y ,w, h = ...