JAVA多线程:Hook (钩子) 线程(六)

发布于:2022-10-14 ⋅ 阅读:(292) ⋅ 点赞:(0)

前言

在多线程的应用中,Hook线程使用的相对较少,然而它依然是多线程中重要的一环,尤其是在释放资源这方面,有着举足轻重的作用,希望本文能解开你心中的疑惑,助你一臂之力!

一、Hook 线程介绍

1.1、作用

如果添加了Hook线程,则JVM程序即将退出的时候(收到了中断信号),Hook 线程在JVM主线程彻底退出之前,就会被执行。

1.2、创建

Runtime.getRuntime().addShutdownHook(new Thread(() -> {
    ...
    //处理方法
}));

二、初识Hook线程

2.1、示例源码

package com.succ.thread.hook;

import java.util.concurrent.TimeUnit;

public class HookThreadDemo {

	public static void creadHookThread(String threadName) {
		Runtime.getRuntime().addShutdownHook(
				  new Thread(()->{
					System.out.println("hook "+threadName+" is running");
					try {
						TimeUnit.SECONDS.sleep(1);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println("hook "+threadName+"  will exit");
				  })
			   );
	}
	public static void main(String[] args) {
		System.out.println("主线程 is running");
		HookThreadDemo.creadHookThread("thread1");
		HookThreadDemo.creadHookThread("thread2");
		System.out.println("主线程 will exit");
	}
}

2.2、效果展示

可以明显看到当主线程即将退出的时候,注入的两个 Hook 线程都被启动并打印相关日志。

三、Hook 线程的应用场景

1、Hook 线程中也可以执行一些关闭、删除、释放资源的操作,比如关闭数据库连接,Socket 连接等。

2、可以做一些校验,防止程序重复执行,以及修复工作。 

四、Hook 线程的注意事项

1、Hook 线程只有在正确接收到退出信号时,才能被正确执行。

通过 kill -9方式,强制杀死的进程,进程是不会去执行 Hook 线程的(自顾不暇,无力他顾)。
2、耗时的操作,不要在 Hook 线程中执行,否则可能会导致程序长时间不能退出。
3、如果有多个hook线程,则它们的执行是无序的。

五、Hook 线程实战演练

5.1、模拟生成lck文件,最后再通过Hook线程删除lck文件的示例

思路:
在程序启动时,校验是否已经生成 lock 文件。

如果已经生成,则退出程序并给出异常信息提示;如果未生成,则生成 lock 文件,程序正常执行,在 JVM 退出的时候,线程中再将 lock 文件删除掉;

注:虚拟机下的CentOS、ZK、Mysql等就采用了这么个机制。

5.1.1、代码示例

package com.succ.thread.hook;

import java.io.File;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

public class LockFileDemo {

	/** .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 线程(在程序退出时,自动删除lock文件)
        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();
    }

}

5.1.2、首次运行main函数后,控制台会一直打印:程序正在运行

5.1.3、点击控制台的红色按钮,或kill -9 强行关闭线程

5.1.4、再次重新运行main函数,则给出提示

5.1.5、在工程源码根目录找到.lock文件,手动删除,再次run main函数

删除lock文件后,发现又可以愉快的玩耍了

后记

文章核心源码来自异常教程,它的开发环境是Linux,本文基于此,以Windows环境为依托,并做了一些完善和优化!

总结

本文详细的介绍了Hook钩子线程是使用,并提供了2个精简的小案例,它的创建和使用都比较简单,重点要记住它的使用场景和注意事项,让我们在合适的开发环境,能够锦上添花。

尾言

不是在学习中前进,就是在前进中摸索,学无止境,每天一个新发现,每天进步一点点,努力少年!

附注

1、JAVA多线程synchronized理论和用法 | Lock和ReentrantLock Volatile 区别和联系(一)

2、JAVA多线程yield/join/wait/notify/notifyAll等方法的作用(二)

3、JAVA多线程狂抓!join()方法到底会不会释放锁,给你彻底介绍清楚(三)

4、JAVA多线程sleep(0)、sleep(1)、sleep(1000)的区别(四) 

5、JAVA多线程揭秘thread.setDaemon(true) | thread.isAlive守护线程的使用(五) 

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到