文章首发自个人微信号: 小哈学Java

个人网站地址: https://www.exception.site/java-concurrency/java-concurrency-hook-thread

目录

  • 一、Hook 线程介绍

  • 二、Hook 线程的应用场景&注意事项

  • 三、Hook 线程防应用重启实战

  • 四、GitHub 源码地址

  • 五、总结

一、Hook 线程介绍

通常情况下,我们可以向应用程序注入一个或多个 Hook (钩子) 线程,这样,在程序即将退出的时候,也就是 JVM 程序即将退出的时候,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 文件是否生成:

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

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

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

到此,Hook 线程代码实战部分结束了。

四、GitHub 源码地址

https://github.com/weiwosuoai/java-concurrent-tutorial

五、总结

本文中,我们学习了什么是 Hook (钩子) 线程,相关应用场景以及注意事项。祝你学习愉快 !

赠送 | 面试&学习福利资源

获取方式: 关注微信公众号: 小哈学Java, 后台回复"666",既可免费无套路获取资源链接,下面是目录以及部分截图:

欢迎关注微信公众号: 小哈学Java

面试官: 说说看, 什么是 Hook (钩子) 线程以及应用场景?的更多相关文章

  1. 面试官:“看你简历上写熟悉 Handler 机制,那聊聊 IdleHandler 吧?”

    一. 序 Handler 机制算是 Android 基本功,面试常客.但现在面试,多数已经不会直接让你讲讲 Handler 的机制,Looper 是如何循环的,MessageQueue 是如何管理 M ...

  2. 面试官:看你简历说写精通ThreadLocal,这几道题你都会吗?

    问题 和Synchronized的区别 存储在jvm的哪个区域 真的只是当前线程可见吗 会导致内存泄漏么 为什么用Entry数组而不是Entry对象 你学习的开源框架哪些用到了ThreadLocal ...

  3. 面试官:说说TCP和UDP的区别和应用场景

    原创文章首发于公众号:「码农富哥」,欢迎收藏和关注,如转载请注明出处! 上一篇聊完 一文彻底搞懂 TCP三次握手.四次挥手过程及原理 这次聊聊TCP和UDP的区别和场景 TCP/IP 中有两个具有代表 ...

  4. 面试官的七种武器:Java篇

    起源 自己经历过的面试也不少了,互联网的.外企的,都有.总结一下这些面试的经验,发现面试官问的问题其实不外乎几个大类,玩不出太多新鲜玩意的.细细想来,面试官拥有以下七种武器.恰似古龙先生笔下的武侠世界 ...

  5. 面试官:你对Redis缓存了解吗?面对这11道面试题你是否有很多问号?

    前言 关于Redis的知识,总结了一个脑图分享给大家 1.在项目中缓存是如何使用的?为什么要用缓存?缓存使用不当会造成什么后果? 面试官心理分析 这个问题,互联网公司必问,要是一个人连缓存都不太清楚, ...

  6. 一个HashMap能跟面试官扯上半个小时

    一个HashMap能跟面试官扯上半个小时 <安琪拉与面试官二三事>系列文章 一个HashMap能跟面试官扯上半个小时 一个synchronized跟面试官扯了半个小时 一个volatile ...

  7. Android面试官:说说你对 Binder 驱动的了解?

    面试官提了一个问题:说说你对 binder 驱动的了解.这个问题虽有些 "面试造火箭" 的无奈,可难点就是亮点.价值所在,是筛选面试者的有效手段.如果让你回答,你能说出多少呢?我们 ...

  8. Redis——面试官考题

    总结: 本文在一次面试的过程中讲述了 Redis 是什么,Redis 的特点和功能,Redis 缓存的使用,Redis 为什么能这么快,Redis 缓存的淘汰策略,持久化的两种方式,Redis 高可用 ...

  9. 面试官:我们来聊一聊Redis吧,你了解多少就答多少

    哈喽!大家好,我是小奇,一位不靠谱的程序员 小奇打算以轻松幽默的对话方式来分享一些技术,如果你觉得通过小奇的文章学到了东西,那就给小奇一个赞吧 文章持续更新,建议收藏关注 一.前言 作为一名Java程 ...

随机推荐

  1. OFFICE2007软件打开word时出现SETUP ERROR的解决方法

    今天打开word时出现以下错误窗口: 在度娘上找了一下解决方案,原来每次打开word时都会启动一些无用的东西,找到这些东西的路径D:\Program Files\Common Files\micros ...

  2. Guava新增集合类型-Bimap

    Guava新增集合类型-Bimap BiMap提供了一种新的集合类型,它提供了key和value的双向关联的数据结构. 通常情况下,我们在使用Java的Map时,往往是通过key来查找value的,但 ...

  3. Hibernate Annotation _List/Map

    // Student.java 实体类 package com.tao.pojo; import java.util.List; public class Student { private int ...

  4. [HNOI2015]菜肴制作 拓扑序

    逆序最大字典序拓扑序 反向建边,逆序字典序最大.. #include<cstdio> #include<cstring> #include<iostream> #i ...

  5. 正则表达式(Regular expressions)使用笔记

    Regular expressions are a powerful language for matching text patterns. This page gives a basic intr ...

  6. spss汉化详解

    今天写一下关于SPSS的汉化以及激活码 下载spss: 安装过程比较简单,主要就是激活码: 9DNCAF2O3QVDV7FBIO696OO6GWLNXZPPRYTPWF2PPX7C8T6Y24LMVV ...

  7. WeTest----如何查看Wetest生成测试报告?

    在前一篇文章中主要介绍了手机中如何开始进行WeTest测试,此篇文章将总结测试报告的查看,这里很简单啦,一切的测试结果wetest都将自动生成,值得注意的是,新注册的用户默认每项测试次数为5,所以请珍 ...

  8. java泛型中使用的排序算法——归并排序及分析

    一.引言 我们知道,java中泛型排序使用归并排序或TimSort.归并排序以O(NlogN)最坏时间运行,下面我们分析归并排序过程及分析证明时间复杂度:也会简述为什么java选择归并排序作为泛型的排 ...

  9. 1. CMake 系列 - 从零构建动态库和静态库

    目录 1. 文件目录结构 2. 库文件源代码 3. 编译生成库文件 1. 文件目录结构 首先创建如下目录结构: └── lib ├── build # ├── CMakeLists.txt └── s ...

  10. 【JVM虚拟机】(9)-- JVM是如何处理异常的

    [JVM虚拟机](9)-- JVM是如何处理异常的 上篇博客我们简单说过异常信息是存放在属性表集合中的Code属性表里,那么这篇博客就单独讲Code属性表中的exception_table. 在讲之前 ...