多线程之hook线程

写在前面

1:干什么的?怎么用的?

  • 干什么的?
    在jvm退出时执行操作。
  • 怎么用的?
    Runtime.getRuntime().addShutdownHook(new Thread...)

2:实例

如下程序程序注册了2个hook:

package dongshi.daddy;

import java.util.concurrent.TimeUnit;

public class Huohuo {
    public static void main(String[] args) {
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            System.out.println("shutdown hook 1 exec...");
        }));
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            System.out.println("shutdown hook 2 exec...");
        }));

        try {
            // 模拟程序正常执行
            TimeUnit.SECONDS.sleep(3);
        } catch (Exception e) {}

        System.out.println("main thread exits...");
    }
}

运行,当main函数退出,此时jvm会退出(没有正在运行的非守护进程了)

bogon:daddy xb$ javac -d . Huohuo.java 
bogon:daddy xb$ java dongshi.daddy.Huohuo
main thread exits...
shutdown hook 1 exec...
shutdown hook 2 exec...

jvm退出时两个钩子线程都执行了。

3:具体应用

可以应用在防止程序重复执行的场景中,在程序运行后先生成一个.lock文件,代表程序已经开始执行了,如果是重复执行,则检测.lock文件存在之后直接报错结束,如下流程图:

多线程之hook线程_第1张图片

程序:

package a.b;

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

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();
    }
}
  • 首次运行正常
bogon:temp xb$ javac -d . PreventDuplicated.java 
bogon:temp xb$ java a.b.PreventDuplicated
The program is running ...
The program is running ...
The program is running ...
The program is running ...
  • 重复运行报错
Exception in thread "main" java.lang.RuntimeException: The program already running.
	at a.b.PreventDuplicated.checkLockFile(PreventDuplicated.java:53)
	at a.b.PreventDuplicated.main(PreventDuplicated.java:18)
  • 停止程序后,再次运行正常
停止程序:
bogon:temp xb$ jps -l
11056 org.jetbrains.idea.maven.server.RemoteMavenServer36
11446 org.jetbrains.jps.cmdline.Launcher
12714 jdk.jcmd/sun.tools.jps.Jps
12607 a.b.PreventDuplicated
10991 
bogon:temp xb$ kill 12607

程序运行窗口输出:

The program is running ...
The program received kill signal. -- 钩子线程执行了

再次运行:
bogon:temp xb$ java a.b.PreventDuplicated
The program is running ...
The program is running ...

看下生成的.lock文件:

bogon:temp xb$ ls -a | grep 'lock'
.lock

4:注意事项

1:kill -9钩子线程将不会执行,因为是强杀,jvm没有执行的机会
2:不要在钩子线程中执行耗时操作,不然会导致,jvm进程长时间无法退出

1: 这种防止程序重复执行的策略,也被应用于 Mysql 服务器,zookeeper, kafka 等系统中。

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

写在后面

参考文章列表

Java 多线程之 Hook (钩子) 线程 。

你可能感兴趣的:(Java高级开发进阶教程,jvm,java,开发语言)