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的加载的…..其实这些都是用到 ...
随机推荐
- Linux:Linux操作防火墙命令
首先查看Linux的防火墙是否关闭 firewall-cmd Linux上新用的防火墙软件,跟iptables差不多的工具. firewall-cmd --state # 显示防火墙状态 system ...
- Vue高阶
Vue.cli是基于vue应用开发提供的一个脚手架工具,为应用搭建基础的框架架构,提供插件.开发服务.打包等功能. 1. 安装 node.js是一个JavaScript的运行环境,提供了一个事件驱动. ...
- Min25 筛学习笔记
仅仅是 \(min25\) 筛最基本的方法,没有任何推式子的例题.(想了想还是加两道吧qwq) 这里解决的是 \(Luogu\) 那道模板题. min25 基本方法: 最基础的是两个式子: \[G(n ...
- Day4 包机制 及JavaDoc文档.
包机制 为了更好地组织类,java提供了包机制,用于区别类名的命名空间. 包的本质是文件夹 它语句的语法格式为: package pkg1[. pkg2 [.pkg3...] ] ; 一般利用公司域名 ...
- Cookie学习总结
Cookie简述 1. 概念 一种客户端会话技术,可以将一些少量的数据保存在客户端. 2. 快速使用 步骤 创建cookie对象,并设定数据 new Cookie(String name, Strin ...
- 分布式ID生成器(CosId)的设计与实现
分布式ID生成器(CosId)设计与实现 CosId 简介 CosId 旨在提供通用.灵活.高性能的分布式 ID 生成器. 目前提供了俩类 ID 生成器: SnowflakeId : 单机 TPS 性 ...
- P4169-CDQ分治/K-D tree(三维偏序)-天使玩偶
P4169-CDQ分治/K-D tree(三维偏序)-天使玩偶 这是一篇两种做法都有的题解 题外话 我写吐了-- 本着不看题解的原则,没写(不会)K-D tree,就写了个cdq分治的做法.下面是我的 ...
- P4774-屠龙勇士-扩展中国剩余定理
屠龙勇士 很久很久以前,巨龙突然出现,带来了灾难带走公主又消失不见.王国十分危险,世间谁最勇敢,一位英雄出现-- 学习于该大佬博客 那么你就是这位英雄,不过不同的是,你面对的是一群巨龙,虽然巨龙都不会 ...
- 【动态规划】石子合并 luogu-1880
分析 简单的区间DP AC代码 #include <bits/stdc++.h> using namespace std; #define ms(a,b) memset(a,b,sizeo ...
- vue3.0使用ant-design-vue进行按需加载原来这么简单
下载 ui库 yarn add ant-design-vue 默认是全局引入,打包后体积很大, 非常影响首屏加载速度, 按需加载 下载按需加载的插件;推荐使用cnpm cnpm install bab ...