亚洲视频二区_亚洲欧洲日本天天堂在线观看_日韩一区二区在线观看_中文字幕不卡一区

公告:魔扣目錄網(wǎng)為廣大站長提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請做好本站友鏈:【 網(wǎng)站目錄:http://www.430618.com 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

在 JAVA 的世界里遨游,如果能擁有一雙善于發(fā)現(xiàn)的眼睛,有很多東西留心去看,外加耐心助力,仔細(xì)去品,往往會(huì)品出不一樣的味道。

通過本次分享,能讓你輕松 get 如下幾點(diǎn),絕對收獲滿滿。

a)如何讓 Java 程序?qū)崿F(xiàn)優(yōu)雅停服?有思想才是硬道理!

 

b)addShutdownHook 的使用場景?會(huì)用才是王道!

 

c)addShutdownHook 鉤子函數(shù)到底是個(gè)啥?刨根問底!

1. 如何讓 Java 程序?qū)崿F(xiàn)優(yōu)雅停服?

無論是自研基礎(chǔ)服務(wù)框架,還是分析開源項(xiàng)目源碼,細(xì)心的 Java 開發(fā)同學(xué),都會(huì)發(fā)現(xiàn) Runtime.getRuntime().addShutdownHook 這么一句代碼的身影,這句到底是干什么用的?

接下來就一起細(xì)品,看看它香不香?

阿里開源的數(shù)據(jù)同步神器 Canal 啟動(dòng)時(shí)的部分源碼:

Java 如何利用鉤子函數(shù)實(shí)現(xiàn)優(yōu)雅停服?刨根問底

 

Apache 麾下的用于海量日志收集的 Flume 啟動(dòng)時(shí)的部分源碼:

Java 如何利用鉤子函數(shù)實(shí)現(xiàn)優(yōu)雅停服?刨根問底

 

仰望了一下開源的項(xiàng)目,不妨從中提煉一下共性(同樣的代碼遇到多次,勢必會(huì)品出味道),寫段代碼跑跑看(站在 flume 源碼的肩膀上,起飛)。

import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 體驗(yàn) Java 優(yōu)雅停服
 *
 * @author 一猿小講
 */
public class Application {

    /**
     * 監(jiān)控服務(wù)
     */
    private ScheduledThreadPoolExecutor monitorService;

    public Application() {
        monitorService = new ScheduledThreadPoolExecutor(1);
    }

    /**
     * 啟動(dòng)監(jiān)控服務(wù),監(jiān)控一下內(nèi)存信息
     */
    public void start() {
        System.out.println(String.format("啟動(dòng)監(jiān)控服務(wù) %s", Thread.currentThread().getId()));
        monitorService.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                System.out.println(String.format("最大內(nèi)存: %dm  已分配內(nèi)存: %dm  已分配內(nèi)存中的剩余空間: %dm  最大可用內(nèi)存: %dm",
                        Runtime.getRuntime().maxMemory() / 1024 / 1024,
                        Runtime.getRuntime().totalMemory() / 1024 / 1024,
                        Runtime.getRuntime().freeMemory() / 1024 / 1024,
                        (Runtime.getRuntime().maxMemory() - Runtime.getRuntime().totalMemory() +
                                Runtime.getRuntime().freeMemory()) / 1024 / 1024));
            }
        }, 2, 2, TimeUnit.SECONDS);
    }

    /**
     * 釋放資源(代碼來源于 flume 源碼)
     * 主要用于關(guān)閉線程池(看不懂的同學(xué)莫糾結(jié),當(dāng)做黑盒去對待)
     */
    public void stop() {
        System.out.println(String.format("開始關(guān)閉線程池 %s", Thread.currentThread().getId()));
        if (monitorService != null) {
            monitorService.shutdown();
            try {
                monitorService.awaitTermination(10, TimeUnit.SECONDS);
            } catch (InterruptedException e) {
                System.err.println("Interrupted while waiting for monitor service to stop");
            }
            if (!monitorService.isTerminated()) {
                monitorService.shutdownNow();
                try {
                    while (!monitorService.isTerminated()) {
                        monitorService.awaitTermination(10, TimeUnit.SECONDS);
                    }
                } catch (InterruptedException e) {
                    System.err.println("Interrupted while waiting for monitor service to stop");
                }
            }
        }
        System.out.println(String.format("線程池關(guān)閉完成 %s", Thread.currentThread().getId()));
    }

    /**
     * 應(yīng)用入口
     */
    public static void main(String[] args) {
        Application application = new Application();
        // 啟動(dòng)服務(wù)(每隔一段時(shí)間監(jiān)控輸出一下內(nèi)存信息)
        application.start();

        // 添加鉤子,實(shí)現(xiàn)優(yōu)雅停服(主要驗(yàn)證鉤子的作用)
        final Application appReference = application;
        Runtime.getRuntime().addShutdownHook(new Thread("shutdown-hook") {
            @Override
            public void run() {
                System.out.println("接收到退出的訊號(hào),開始打掃戰(zhàn)場,釋放資源,完成優(yōu)雅停服");
                appReference.stop();
            }
        });
        System.out.println("服務(wù)啟動(dòng)完成");
    }
}

經(jīng)常讀文的我很清楚,耐心讀文章中源碼的同學(xué)應(yīng)該很少,所以我還是用圖給你簡單捋一捋。

Java 如何利用鉤子函數(shù)實(shí)現(xiàn)優(yōu)雅停服?刨根問底

 

標(biāo)注1:start 方法利用線程池啟動(dòng)一個(gè)線程去定時(shí)監(jiān)控內(nèi)存信息;

標(biāo)注2:stop 方法用于在退出程序之前,進(jìn)行關(guān)閉線程池進(jìn)而釋放資源。

程序跑起來,效果如下。

Java 如何利用鉤子函數(shù)實(shí)現(xiàn)優(yōu)雅停服?刨根問底

 

當(dāng)進(jìn)行 kill 操作時(shí),程序確實(shí)進(jìn)行了資源釋放,效果確實(shí)很優(yōu)雅。

Java 如何利用鉤子函數(shù)實(shí)現(xiàn)優(yōu)雅停服?刨根問底

 

一切看似那么自然,一切又是那么完美,這是真的嗎?殺進(jìn)程時(shí)候如果用 kill -9,這種情況下會(huì)發(fā)生什么現(xiàn)象呢?

Java 如何利用鉤子函數(shù)實(shí)現(xiàn)優(yōu)雅停服?刨根問底

 

嗚呼!結(jié)果不會(huì)騙人的,當(dāng)用 kill -9 的時(shí)候,就顯得很粗暴了,壓根不管什么資源釋放,不管三七二十一,就是終止程序。

估計(jì)很多同學(xué),都擅長用 kill -9 進(jìn)行殺進(jìn)程,為了線上的應(yīng)用安全,還是用 kill -15 命令殺進(jìn)程吧,這樣會(huì)給應(yīng)用留點(diǎn)時(shí)間去打掃一下戰(zhàn)場,釋放一下資源。

好了,通過仔細(xì)品味,借助 JDK 自帶的 addShutdownHook 來助力應(yīng)用,確實(shí)能讓線上服務(wù)跑起來很優(yōu)雅。

有思想才是硬道理!

2. addShutdownHook 的使用場景?

通過代碼試驗(yàn),能夠感知 addShutdownHook(new Thread(){}) 是 JVM 銷毀前要執(zhí)行的一個(gè)線程,那么只要是涉及到資源回收的場景,應(yīng)該都可以滿足,下面簡單列舉幾個(gè)。

a)數(shù)據(jù)同步神器 Canal 借助它,來進(jìn)行關(guān)閉 socket 鏈接、釋放 canal 的工作節(jié)點(diǎn)、清理緩存信息等;

 

b)海量日志收集 Flume 借助它,來實(shí)現(xiàn)線程池資源關(guān)閉、工作線程停止等;

 

c)在應(yīng)用正常退出時(shí),執(zhí)行特定的業(yè)務(wù)邏輯、關(guān)閉資源等操作。

 

d)在 OOM 宕機(jī)、 CTRL+C、或執(zhí)行 kill pid,導(dǎo)致 JVM 非正常退出時(shí),加入必要的挽救措施成為可能。

其實(shí),在 Java 的世界里遨游,只有想不到的,沒有做不到的!

3. addShutdownHook 鉤子函數(shù)是個(gè)啥?

刨根還要問到底!

Java 如何利用鉤子函數(shù)實(shí)現(xiàn)優(yōu)雅停服?刨根問底

 

Hook 翻譯過來是「鉤子」的意思,那顧名思義就是用來掛東西的。

Java 如何利用鉤子函數(shù)實(shí)現(xiàn)優(yōu)雅停服?刨根問底

 

如圖所示,在現(xiàn)實(shí)生活中,要制作臘肉,首先用鉤子把肉勾住,然后掛在竹竿上,這應(yīng)該是鉤子的作用。

生活如此,一切設(shè)計(jì)理念都源于生活,在 Java 的世界里,亦是如此。

Java 如何利用鉤子函數(shù)實(shí)現(xiàn)優(yōu)雅停服?刨根問底

 

如上圖 Runtime 的源碼所示,遵循 Java 的核心思想「一切皆是對象」,那么可以把 addShutdownHook 方法可以視作掛鉤子,其實(shí)稱之為鉤子函數(shù)會(huì)好一些,而現(xiàn)實(shí)生活中的肉就可以抽象為釋放資源的線程。

只要有這個(gè)鉤子函數(shù),對外就提供了擴(kuò)展能力,研發(fā)人員就可以往鉤子上掛各種自定義的場景實(shí)現(xiàn),這種設(shè)計(jì)你細(xì)品那絕對是香!這也就是 Canal、Flume、Tomcat 等不同應(yīng)用,在優(yōu)雅停服時(shí)有著不同的實(shí)現(xiàn)的原因吧。

大白話,鉤子函數(shù)有了,想掛什么東西,根據(jù)心情自己定就好了。

再深入去刨會(huì)發(fā)現(xiàn),由于底層數(shù)據(jù)結(jié)構(gòu)采用 Map 來進(jìn)行存儲(chǔ),那么就支持研發(fā)人員掛多個(gè) shutdownHook 的實(shí)現(xiàn),又帶來了無限的可能性(又帶來了無限的「刺激」,自己好好去體會(huì))。

Java 如何利用鉤子函數(shù)實(shí)現(xiàn)優(yōu)雅停服?刨根問底

 

好了,避免頭大,就刨到這兒吧,感興趣的可自行順著思路繼續(xù)刨下去。

4. 寄語,寫在最后

作為研發(fā)人員:要擁有一雙善于發(fā)現(xiàn)的眼睛,要善于發(fā)現(xiàn)代碼之美。

作為研發(fā)人員:要時(shí)常思考面對當(dāng)前的項(xiàng)目,是否能夠簡單重構(gòu)讓程序跑的更順溜。

作為研發(fā)人員:要多看、多悟、多提煉、多實(shí)踐。

作為研發(fā)人員:請不要放棄代碼,因?yàn)槌绦蚪K會(huì)鑄就人生。

本次分享就到這里,希望對你有所幫助吧。

一起聊技術(shù)、談業(yè)務(wù)、噴架構(gòu),少走彎路,不踩大坑。歡迎關(guān)注「一猿小講」,會(huì)持續(xù)輸出原創(chuàng)精彩分享,敬請期待!

分享到:
標(biāo)簽:鉤子 函數(shù) Java
用戶無頭像

網(wǎng)友整理

注冊時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績評定2018-06-03

通用課目體育訓(xùn)練成績評定