新聞中心
一、Hook 線程介紹
通常情況下,我們可以向應用程序注入一個或多個 Hook (鉤子) 線程,這樣,在程序即將退出的時候,也就是 JVM 程序即將退出的時候,Hook 線程就會被啟動執(zhí)行。
成都創(chuàng)新互聯(lián)公司一直秉承“誠信做人,踏實做事”的原則,不欺瞞客戶,是我們最起碼的底線! 以服務為基礎,以質量求生存,以技術求發(fā)展,成交一個客戶多一個朋友!為您提供成都網(wǎng)站建設、成都網(wǎng)站設計、成都網(wǎng)頁設計、微信小程序、成都網(wǎng)站開發(fā)、成都網(wǎng)站制作、成都軟件開發(fā)、重慶APP軟件開發(fā)是成都本地專業(yè)的網(wǎng)站建設和網(wǎng)站設計公司,等你一起來見證!
先看一段示例代碼:
①:為應用程序注入一個鉤子(Hook)線程,線程中,打印了相關日志,包括正在運行以及退出的日志;
②:再次注入一個同樣邏輯的鉤子(Hook)線程;
③:主線程執(zhí)行結束,打印日志;
運行這段代碼,來驗證一下:
從打印日志看到,當主線程執(zhí)行結束,也就是 JVM 進程即將退出的時候,注入的兩個 Hook 線程都被啟動并打印相關日志。
二、Hook 線程的應用場景&注意事項
2.1 應用場景
上面我們已經(jīng)知道了, Hook 線程能夠在 JVM 程序退出的時候被啟動且執(zhí)行,那么,我們能夠通過這種特性,做點什么呢?
羅列一些常見應用場景:
防止程序重復執(zhí)行,具體實現(xiàn)可以在程序啟動時,校驗是否已經(jīng)生成 lock 文件,如果已經(jīng)生成,則退出程序,如果未生成,則生成 lock 文件,程序正常執(zhí)行,最后再注入 Hook 線程,這樣在 JVM 退出的時候,線程中再將 lock 文件刪除掉;
PS: 這種防止程序重復執(zhí)行的策略,也被應用于 MySQL 服務器,zookeeper, kafka 等系統(tǒng)中。
Hook 線程中也可以執(zhí)行一些資源釋放的操作,比如關閉數(shù)據(jù)庫連接,Socket 連接等。
2.2 注意事項
- Hook 線程只有在正確接收到退出信號時,才能被正確執(zhí)行,如果你是通過 kill -9這種方式,強制殺死的進程,那么抱歉,進程是不會去執(zhí)行 Hook 線程的,為什么呢?你想啊,它自己都被強制干掉了,哪里還管的上別人呢?
- 請不要在 Hook 線程中執(zhí)行一些耗時的操作,這樣會導致程序長時間不能退出。
三、Hook 線程防應用重啟實戰(zhàn)
針對上面防應用重啟的場景,利用 Hook 線程,我們來實戰(zhàn)一下,貼上代碼:
import java.io.File;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
/**
* @author 小澤java
* @date 2019/4/15
* @time 下午3: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 文件是否已經(jīng)存在
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 文件是否已經(jīng)存在
*/
private static void checkLockFile() {
if (isLockFileExisted()) {
// .lock 文件已存在, 拋出異常, 退出程序
throw new RuntimeException("The program already running.");
}
// 不存在,則創(chuàng)建 .lock 文件
createLockFile();
}
/**
* 創(chuàng)建 .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 線程代碼實戰(zhàn)部分結束了。
當前標題:面試官:什么是Hook(鉤子)線程以及應用場景?
文章分享:http://fisionsoft.com.cn/article/pigdij.html