最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java并發(fā)編程之如何優(yōu)雅關(guān)閉鉤子Shutdown Hook

 更新時(shí)間:2025年04月29日 15:35:11   作者:寫bug寫bug  
這篇文章主要為大家詳細(xì)介紹了Java如何實(shí)現(xiàn)優(yōu)雅關(guān)閉鉤子Shutdown Hook,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下

關(guān)閉鉤子簡介

當(dāng)程序即將退出時(shí)(例如釋放資源、關(guān)閉數(shù)據(jù)庫連接等),可以通過預(yù)先注冊一個(gè)或多個(gè)**關(guān)閉鉤子線程(Shutdown Hook)**來執(zhí)行相關(guān)操作。當(dāng) JVM 進(jìn)程準(zhǔn)備退出時(shí),這些鉤子線程會(huì)被觸發(fā)并運(yùn)行。

示例代碼:

public class HookThreadDemo {
    privatestaticclass HookRunnable implements Runnable {
        @Override
        public void run() {
            try {
                System.out.println("鉤子線程 " + Thread.currentThread().getName() + " 正在執(zhí)行...");
                Thread.sleep(2000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("鉤子線程 " + Thread.currentThread().getName() + " 執(zhí)行結(jié)束");
        }
    }

    public static void main(String[] args) {
        HookRunnable hookRunnable = new HookRunnable();
        // 添加鉤子線程 0
        Runtime.getRuntime().addShutdownHook(new Thread(hookRunnable));
        // 添加鉤子線程 1
        Runtime.getRuntime().addShutdownHook(new Thread(hookRunnable));
        System.out.println("主線程即將結(jié)束執(zhí)行");
    }
}

輸出結(jié)果:

主線程即將結(jié)束執(zhí)行
鉤子線程 Thread-0 正在執(zhí)行...
鉤子線程 Thread-1 正在執(zhí)行...
鉤子線程 Thread-1 執(zhí)行結(jié)束
鉤子線程 Thread-0 執(zhí)行結(jié)束

當(dāng)主線程執(zhí)行完畢后,JVM 進(jìn)程退出前,所有注冊的鉤子線程會(huì)被啟動(dòng)并執(zhí)行。

關(guān)閉鉤子應(yīng)用場景

釋放資源:關(guān)閉文件句柄、數(shù)據(jù)庫連接等,避免資源泄漏。

停止服務(wù):安全關(guān)閉服務(wù)器,確保所有請求處理完畢。

發(fā)送通知:通過郵件或短信通知用戶服務(wù)已停止。

記錄日志:保存系統(tǒng)狀態(tài)或錯(cuò)誤信息,便于后續(xù)排查問題。

數(shù)據(jù)庫連接實(shí)戰(zhàn)演示

以下代碼演示如何用關(guān)閉鉤子關(guān)閉數(shù)據(jù)庫連接:

public class DatabaseConnection {
    privatestatic Connection conn;

    public static void main(String[] args) {
        System.out.println("主線程開始執(zhí)行");
        initConnection(); // 初始化數(shù)據(jù)庫連接
        System.out.println("執(zhí)行數(shù)據(jù)查詢與處理");

        // 注冊關(guān)閉鉤子
        Runtime.getRuntime().addShutdownHook(new Thread(() -> closeConnection()));
        System.out.println("主線程結(jié)束執(zhí)行");
    }

    private static void initConnection() {
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            conn = DriverManager.getConnection(
                "jdbc:mysql://localhost:3306/school_info?useSSL=true&",
                "root", "root"
            );
            System.out.println("數(shù)據(jù)庫連接成功!");
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        }
    }

    private static void closeConnection() {
        try {
            conn.close();
            System.out.println("數(shù)據(jù)庫連接已關(guān)閉!");
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

輸出結(jié)果:

主線程開始執(zhí)行
數(shù)據(jù)庫連接成功!
執(zhí)行數(shù)據(jù)查詢與處理
主線程結(jié)束執(zhí)行
數(shù)據(jù)庫連接已關(guān)閉!

使用關(guān)閉鉤子的注意事項(xiàng)

  • 強(qiáng)制終止進(jìn)程(如kill -9)不會(huì)觸發(fā)鉤子線程。
  • 避免耗時(shí)操作:鉤子線程中不要執(zhí)行長時(shí)間任務(wù),否則會(huì)延遲 JVM 退出。
  • 禁止異常拋出:鉤子線程中的異??赡軐?dǎo)致 JVM 無法正常退出。
  • 注冊順序:按依賴關(guān)系注冊鉤子,先注冊簡單任務(wù),后注冊復(fù)雜任務(wù)。
  • 避免啟動(dòng)新線程:在鉤子中啟動(dòng)新線程可能導(dǎo)致 JVM 無法正常關(guān)閉。

開源框架中的關(guān)閉鉤子機(jī)制

1. Spring

AbstractApplicationContext中,registerShutdownHook()方法注冊鉤子,用于關(guān)閉上下文:

public void registerShutdownHook() {
    if (this.shutdownHook == null) {
        this.shutdownHook = new Thread(() -> doClose());
        Runtime.getRuntime().addShutdownHook(this.shutdownHook);
    }
}

2. Tomcat

Tomcat 通過注冊鉤子確保服務(wù)關(guān)閉時(shí)釋放資源:

public void registerShutdownHook() {
    if (this.shutdownHook == null) {
        this.shutdownHook = new Thread(() -> {
            synchronized (startupShutdownMonitor) {
                doClose();
            }
        });
        Runtime.getRuntime().addShutdownHook(this.shutdownHook);
    }
}

關(guān)閉鉤子機(jī)制的原理

JVM 啟動(dòng)時(shí),主線程會(huì)創(chuàng)建一個(gè)關(guān)閉線程(Shutdown Thread),并將所有注冊的鉤子添加到其任務(wù)列表中。當(dāng) JVM 收到終止信號時(shí):

  • 停止所有用戶線程。
  • 啟動(dòng)關(guān)閉線程,按順序執(zhí)行鉤子任務(wù)。
  • 等待所有鉤子執(zhí)行完畢或超時(shí)后退出。

鉤子的注冊與執(zhí)行

注冊:通過Runtime.getRuntime().addShutdownHook(Thread)將線程添加到ApplicationShutdownHooks的靜態(tài)列表中。

執(zhí)行:關(guān)閉線程按順序同步執(zhí)行系統(tǒng)級鉤子,異步執(zhí)行應(yīng)用級鉤子,并等待所有線程完成。

關(guān)閉鉤子的觸發(fā)時(shí)機(jī)

  • 主動(dòng)調(diào)用:通過Runtime.exit()System.exit()觸發(fā)。
  • 信號捕獲:JVM 注冊信號處理器(如INT、TERM),捕獲kill命令發(fā)送的信號后觸發(fā)。

示例代碼(捕獲信號):

public class SignalHandlerTest implements SignalHandler {
    public static void main(String[] args) {
        Runtime.getRuntime().addShutdownHook(new Thread(() -> 
            System.out.println("關(guān)閉鉤子正在運(yùn)行...")));

        SignalHandler handler = new SignalHandlerTest();
        Signal.handle(new Signal("INT"), handler);  // 捕獲 Ctrl+C
        Signal.handle(new Signal("TERM"), handler); // 捕獲 kill 命令

        while (true) {
            System.out.println("主線程運(yùn)行中...");
            Thread.sleep(2000);
        }
    }

    @Override
    public void handle(Signal signal) {
        System.out.println("接收到信號:" + signal.getName() + "-" + signal.getNumber());
        System.exit(0);
    }
}

輸出示例:

主線程運(yùn)行中...
主線程運(yùn)行中...
^C接收到信號:INT-2
關(guān)閉鉤子正在運(yùn)行...

信號處理與守護(hù)線程

信號不可捕獲的情況KILL(9)和QUIT(3)無法被捕獲。

守護(hù)線程:JVM 在所有用戶線程結(jié)束后自動(dòng)退出,守護(hù)線程(如 GC 線程)不會(huì)阻止 JVM 退出。

總結(jié)

Java 的關(guān)閉鉤子機(jī)制覆蓋了大部分退出場景,但以下情況例外:

  • 使用kill -9強(qiáng)制終止進(jìn)程時(shí),鉤子不會(huì)執(zhí)行。
  • 信號處理需調(diào)用System.exit()確保進(jìn)程退出。

通過合理使用關(guān)閉鉤子,可以實(shí)現(xiàn)資源釋放、服務(wù)優(yōu)雅關(guān)閉等關(guān)鍵功能。

到此這篇關(guān)于Java并發(fā)編程之如何優(yōu)雅關(guān)閉鉤子Shutdown Hook的文章就介紹到這了,更多相關(guān)Java關(guān)閉鉤子內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論

定襄县| 溧阳市| 平江县| 昌图县| 新化县| 屯留县| 平泉县| 敖汉旗| 宁城县| 穆棱市| 内黄县| 大姚县| 沙湾县| 静海县| 历史| 波密县| 华蓥市| 扶沟县| 县级市| 横山县| 保德县| 双桥区| 信丰县| 邯郸市| 商城县| 德州市| 吴江市| 同德县| 六枝特区| 卫辉市| 鄯善县| 永昌县| 盖州市| 南雄市| 怀安县| 陆河县| 阿勒泰市| 永靖县| 湘阴县| 西乡县| 图木舒克市|