java|面试官: 说说看, 什么是 Hook (钩子) 线程以及应用场景?

目录

  • 一、Hook 线程介绍
  • 二、Hook 线程的应用场景&注意事项
  • 三、Hook 线程防应用重启实战
  • 四、GitHub 源码地址
  • 五、总结
一、Hook 线程介绍 通常情况下,我们可以向应用程序注入一个或多个 Hook (钩子) 线程,这样,在程序即将退出的时候,也就是 JVM 程序即将退出的时候,Hook 线程就会被启动执行。
先看一段示例代码:


示例代码

  • ①:为应用程序注入一个钩子(Hook)线程,线程中,打印了相关日志,包括正在运行以及退出的日志;
  • ②:再次注入一个同样逻辑的钩子(Hook)线程;
  • ③:主线程执行结束,打印日志;
运行这段代码,来验证一下:


Hook 线程执行结果

从打印日志看到,当主线程执行结束,也就是 JVM 进程即将退出的时候,注入的两个 Hook 线程都被启动并打印相关日志。
二、Hook 线程的应用场景&注意事项 2.1 应用场景
上面我们已经知道了, Hook 线程能够在 JVM 程序退出的时候被启动且执行,那么,我们能够通过这种特性,做点什么呢?
罗列一些常见应用场景:
  1. 防止程序重复执行,具体实现可以在程序启动时,校验是否已经生成 lock 文件,如果已经生成,则退出程序,如果未生成,则生成 lock 文件,程序正常执行,最后再注入 Hook 线程,这样在 JVM 退出的时候,线程中再将 lock 文件删除掉;


流程图

PS: 这种防止程序重复执行的策略,也被应用于 Mysql 服务器,zookeeper, kafka 等系统中。
  1. Hook 线程中也可以执行一些资源释放的操作,比如关闭数据库连接,Socket 连接等。
2.2 注意事项
  1. Hook 线程只有在正确接收到退出信号时,才能被正确执行,如果你是通过 kill -9这种方式,强制杀死的进程,那么抱歉,进程是不会去执行 Hook 线程的,为什么呢?你想啊,它自己都被强制干掉了,哪里还管的上别人呢?
  2. 请不要在 Hook 线程中执行一些耗时的操作,这样会导致程序长时间不能退出。
三、Hook 线程防应用重启实战 针对上面防应用重启的场景,利用 Hook 线程,我们来实战一下,贴上代码:
import java.io.File; import java.io.IOException; import java.util.concurrent.TimeUnit; /** * @author 犬小哈(微信号: 小哈学Java) * @date 2019/4/10 * @time 下午9:56 * @discription **/ public class PreventDuplicated {/** .lock 文件存放路径 */ private static final String LOCK_FILE_PATH = "./"; /** .lock 文件名称 */ private static final String LOCK_FILE_NAME = ".lock"; public static void main(String[] args) {// 校验 .lock 文件是否已经存在 checkLockFile(); // 注入 Hook 线程 addShutdownHook(); // 模拟程序一直运行 for (; ; ) { try { TimeUnit.SECONDS.sleep(1); System.out.println("The program is running ..."); } catch (InterruptedException e) { e.printStackTrace(); } }}/** * 注入 Hook 线程 */ private static void addShutdownHook() { Runtime.getRuntime().addShutdownHook(new Thread(() -> { // 接受到了退出信号 System.out.println("The program received kill signal."); // 删除 .lock 文件 deleteLockFile(); })); }/** * 校验 .lock 文件是否已经存在 */ private static void checkLockFile() { if (isLockFileExisted()) { // .lock 文件已存在, 抛出异常, 退出程序 throw new RuntimeException("The program already running."); }// 不存在,则创建 .lock 文件 createLockFile(); }/** * 创建 .lock 文件 */ private static void createLockFile() { File file = new File(LOCK_FILE_PATH + LOCK_FILE_NAME); try { file.createNewFile(); } catch (IOException e) { e.printStackTrace(); } }/** * .lock 文件 是否存在 * @return */ private static boolean isLockFileExisted() { File file = new File(LOCK_FILE_PATH + LOCK_FILE_NAME); return file.exists(); }/** * 删除 .lock 文件 */ private static void deleteLockFile() { File file = new File(LOCK_FILE_PATH + LOCK_FILE_NAME); file.delete(); } }复制代码

运行程序,控制台输出如下:


控制台输出

程序一直运行中,再来看下 .lock 文件是否生成:


lock 文件

文件生成成功,接下来,我们再次运行程序,看看是否能够重复启动:


重复启动程序,抛出异常

可以看到,无法重复运行程序,且抛出了 The program already running. 的运行时异常。接下来,通过 kill pid 或者 kill -l pid 命令来结束进程:


Hook 线程被启动了

程序在即将退出的时候,启动了 Hook 线程,在看下 .lock 文件是否已被删除:


总结:想成为进阶架构师,那么资源肯定也是少不了的,如果你对技术提升很感兴趣,小编在这里提供了一份针对架构进阶视频资料,资源分享包括但不限于(分布式架构、高可扩展、高性能、高并 发、Jvm性能调优、Spring,MyBatis,Nginx源码分析,Redis,ActiveMQ、、Mycat、Netty、Kafka、Mysql 、Zookeeper、Tomcat、Docker、Dubbo、Nginx)。

群内提供免费的Java架构学习资料,QQ群:643459718
【java|面试官: 说说看, 什么是 Hook (钩子) 线程以及应用场景?】java|面试官: 说说看, 什么是 Hook (钩子) 线程以及应用场景?
文章图片

    推荐阅读