創(chuàng)建線程以及線程池時候要指定與業(yè)務相關的名字虑绵,以便于追溯問題

3.9 創(chuàng)建線程以及線程池時候要指定與業(yè)務相關的名字冶共,以便于追溯問題

日常開發(fā)中當一個應用中需要創(chuàng)建多個線程或者線程池時候最好給每個線程或者線程池根據(jù)業(yè)務類型設置具體的名字,以便在出現(xiàn)問題時候方便進行定位邑滨,下面就通過實例來說明不設置時候為何難以定位問題日缨,以及如何進行設置。

3.9.1創(chuàng)建線程需要帶線程名

下面通過簡單的代碼來說明不指定線程名稱為何難定位問題掖看,代碼如下:

    public static void main(String[] args) {
       //訂單模塊
        Thread threadOne = new Thread(new Runnable() {
            public void run() {
                System.out.println("保存訂單的線程");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                throw new NullPointerException();
            }
        });
     //發(fā)貨模塊
        Thread threadTwo = new Thread(new Runnable() {
            public void run() {
                System.out.println("保存收獲地址的線程");
            }
        });

        threadOne.start();
        threadTwo.start();

    }

如上代碼分別創(chuàng)建了線程one和線程two并且啟動執(zhí)行運行上面代碼可能會輸出如下:


image.png

從運行接口可知Thread-0拋出了NPE異常匣距,那么單看這個日志根本無法判斷是訂單模塊的線程拋出的異常,首先我們分析下這個Thread-0是怎么來的哎壳,這要看下創(chuàng)建線程時候的代碼:

    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
        init(g, target, name, stackSize, null);
    }
    

可知如果調(diào)用了沒有指定線程名字的方法創(chuàng)建了線程毅待,內(nèi)部會使用"Thread-" + nextThreadNum()作為線程的默認名字,其中nextThreadNum代碼如下:

    private static int threadInitNumber;
    private static synchronized int nextThreadNum() {
        return threadInitNumber++;
    }

可知threadInitNumber是static變量归榕,nextThreadNum是static方法尸红,所以線程的編號是全應用唯一的并且是遞增的,另外這里由于涉及到了多線程遞增threadInitNumber也就是執(zhí)行讀取-遞增-寫入操作刹泄,而這個是線程不安全的所以使用了方法級別的synchronized進行同步外里。

當一個系統(tǒng)中有多個業(yè)務模塊而每個模塊中有都是用了自己的線程,除非拋出與業(yè)務相關的異常特石,否者比如上面拋出的NPE異常盅蝗,根本沒法判斷是哪一個模塊出現(xiàn)了問題,現(xiàn)在修改代碼如下:

    static final String THREAD_SAVE_ORDER = "THREAD_SAVE_ORDER";
    static final String THREAD_SAVE_ADDR = "THREAD_SAVE_ADDR";

    public static void main(String[] args) {
        // 訂單模塊
        Thread threadOne = new Thread(new Runnable() {
            public void run() {
                System.out.println("保存訂單的線程");
                throw new NullPointerException();
            }
        }, THREAD_SAVE_ORDER);
        // 發(fā)貨模塊
        Thread threadTwo = new Thread(new Runnable() {
            public void run() {
                System.out.println("保存收貨地址的線程");
            }
        }, THREAD_SAVE_ADDR);

        threadOne.start();
        threadTwo.start();

    }

如上代碼在創(chuàng)建線程的時候給線程指定了一個與具體業(yè)務模塊相關的名字姆蘸,下面運行結(jié)果輸出為:


image.png

從運行結(jié)果就可以定位到是保存訂單模塊拋出了NPE異常墩莫,一下子就可以定位到問題。

3.9.2創(chuàng)建線程池時候也需要指定線程池的名稱

同理下面通過簡單的代碼來說明不指定線程池名稱為何難定位問題乞旦,代碼如下:

    static ThreadPoolExecutor executorOne = new ThreadPoolExecutor(5, 5, 1, TimeUnit.MINUTES, new LinkedBlockingQueue<>());
    static ThreadPoolExecutor executorTwo = new ThreadPoolExecutor(5, 5, 1, TimeUnit.MINUTES, new LinkedBlockingQueue<>());

    public static void main(String[] args) {

        //接受用戶鏈接模塊
        executorOne.execute(new  Runnable() {
            public void run() {
                System.out.println("接受用戶鏈接線程");
                throw new NullPointerException();
            }
        });
        //具體處理用戶請求模塊
        executorTwo.execute(new  Runnable() {
            public void run() {
                System.out.println("具體處理業(yè)務請求線程");
            }
        });
        
        executorOne.shutdown();
        executorTwo.shutdown();
    }

運行代碼輸出如下結(jié)果:


同理我們并不知道是那個模塊的線程池拋出了這個異常贼穆,那么我們看下這個pool-1-thread-1是如何來的。其實是使用了線程池默認的ThreadFactory兰粉,翻看線程池創(chuàng)建的源碼如下:

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }
    
   public static ThreadFactory defaultThreadFactory() {
   return new DefaultThreadFactory();
    }
    
 static class DefaultThreadFactory implements ThreadFactory {
        //(1)
        private static final AtomicInteger poolNumber = new AtomicInteger(1);
        private final ThreadGroup group;
        //(2)
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        //(3)
        private final String namePrefix;

        DefaultThreadFactory() {
            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() :
                                  Thread.currentThread().getThreadGroup();
            namePrefix = "pool-" +
                          poolNumber.getAndIncrement() +
                         "-thread-";
        }

        public Thread newThread(Runnable r) {
           //(4)
            Thread t = new Thread(group, r,
                                  namePrefix + threadNumber.getAndIncrement(),
                                  0);
            if (t.isDaemon())
                t.setDaemon(false);
            if (t.getPriority() != Thread.NORM_PRIORITY)
                t.setPriority(Thread.NORM_PRIORITY);
            return t;
        }
    }

如上代碼DefaultThreadFactory的實現(xiàn)可知:

  • 代碼(1)poolNumber是static的原子變量用來記錄當前線程池的編號是應用級別的故痊,所有線程池公用一個,比如創(chuàng)建第一個線程池時候線程池編號為1玖姑,創(chuàng)建第二個線程池時候線程池的編號為2愕秫,這里pool-1-thread-1里面的pool-1中的1就是這個值
  • 代碼(2)threadNumber是線程池級別的慨菱,每個線程池有一個該變量用來記錄該線程池中線程的編號,這里pool-1-thread-1里面的thread-1中的1就是這個值
  • 代碼(3)namePrefix是線程池中線程的前綴戴甩,默認固定為pool
  • 代碼(4)具體創(chuàng)建線程符喝,可知線程的名稱使用namePrefix + threadNumber.getAndIncrement()拼接的。

從上知道我們只需對實現(xiàn)ThreadFactory并對DefaultThreadFactory的代碼中namePrefix的初始化做手腳甜孤,當需要創(chuàng)建線程池是傳入與業(yè)務相關的namePrefix名稱就可以了协饲,代碼如下:

    // 命名線程工廠
    static class NamedThreadFactory implements ThreadFactory {
        private static final AtomicInteger poolNumber = new AtomicInteger(1);
        private final ThreadGroup group;
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;

        NamedThreadFactory(String name) {

            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
            if (null == name || name.isEmpty()) {
                name = "pool";
            }

            namePrefix = name + "-" + poolNumber.getAndIncrement() + "-thread-";
        }

        public Thread newThread(Runnable r) {
            Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
            if (t.isDaemon())
                t.setDaemon(false);
            if (t.getPriority() != Thread.NORM_PRIORITY)
                t.setPriority(Thread.NORM_PRIORITY);
            return t;
        }
    }

然后創(chuàng)建線程池時候如下:

        static ThreadPoolExecutor executorOne = new ThreadPoolExecutor(5, 5, 1, TimeUnit.MINUTES,
            new LinkedBlockingQueue<>(), new NamedThreadFactory("ASYN-ACCEPT-POOL"));
    static ThreadPoolExecutor executorTwo = new ThreadPoolExecutor(5, 5, 1, TimeUnit.MINUTES,
            new LinkedBlockingQueue<>(), new NamedThreadFactory("ASYN-PROCESS-POOL"));

然后運行執(zhí)行結(jié)果如下:
image.png

ASYN-ACCEPT-POOL-1-thread-1就可以知道是接受鏈接線程池拋出的異常。

3.9.3總結(jié)

本節(jié)通過簡單的例子介紹了為何不給線程或者線程池起名字會給問題排查帶來麻煩缴川,然后通過源碼原理介紹線程和線程池名稱是默認名稱是如何來的茉稠,以及如何自定義線程池名稱,以便問題追溯把夸。

--------------------------------相約GitChat探討技術--------------------------------------

一而线、常用開源框架 Spring 和 Tomcat 擴展接口揭秘

評價一個框架是否優(yōu)秀,其中必有一點是看該框架是否留足了可擴展的接口恋日。我們在實際做項目或者研發(fā)框架時膀篮,很多情況下就是在框架留出的擴展接口上進行定制,所以很有必要對這些框架留出了哪些擴展點岂膳,這些擴展點是干啥用的有個心知肚明的了解誓竿。

本 Chat 將針對 Spring 和 Tomcat 擴展點進行介紹,主要內(nèi)容包括:

  • 對 Spring 框架在容器刷新(Refresh 階段)闷营,創(chuàng)建 Bean(getBean)烤黍,容器銷毀(destory)階段中的擴展接口進行講解;

  • 對 Tomcat 中的 ContextLoaderListener 擴展接口進行講解傻盟,并講解 Webx 框架和 SpringMVC 框架如何使用它,從而讓 Tomcat 與應用框架聯(lián)系起來嫂丙。


    image.png
## 二娘赴、SpringBoot核心模塊原理分析Chat

最近微服務很火,SpringBoot 以其輕量級跟啤,內(nèi)嵌 Web 容器诽表,一鍵啟動,方便調(diào)試等特點被越來越多的微服務實踐者所采用隅肥。然而知其然還要知其所以然竿奏,本節(jié)就來講解 SpringBoot 的核心模塊的實現(xiàn)原理,這些內(nèi)容在面試的時候也是會被經(jīng)常問到的:

  • spring-boot-load 模塊腥放,正常情況下一個類加載器只能找到加載路徑的jar包里面當前目錄或者文件類里面的*.class文件泛啸,SpringBoot 允許我們使用 java -jar archive.jar 運行包含嵌套依賴 jar 的 jar 或者 war 文件,那么 SpringBoot 是如何實現(xiàn)的那秃症?

  • spring-boot-autoconfigure 模塊候址,Auto-configuration 是 SpringBoot 在 Spring 的基礎上提供的一個自動掃描 jar 包里面指定注解的類并注入到 Spring 容器的功能組件吕粹。

  • spring-boot 模塊,提供了一些特性用來支持 SpringBoot 中其它模塊岗仑。

    歡迎長按識別二維碼加入本chat

image.png

三匹耕、Java 類加載器揭秘Chat

類加載器作為 JVM 加載字節(jié)碼到內(nèi)存中的媒介,其重要性不言而喻荠雕,另外在職場面試時候也會被頻繁的問道稳其,了解類加載器的原理,能靈活的自定義類加載器去實現(xiàn)自己的功能顯得尤為重要炸卑。

主要內(nèi)容:

  • 講解 Java 中自帶的三種類加載器欢际,以及構(gòu)造原理

  • 講解類加載器原理

  • 講解一種特殊的與線程相關類加載器

  • 講解 Tomcat 框架中多級類加載器的實現(xiàn)原理

  • 講解如何自定義類加載器實現(xiàn)模塊隔離

    歡迎長按識別二維碼加入本chat

image

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市矾兜,隨后出現(xiàn)的幾起案子损趋,更是在濱河造成了極大的恐慌,老刑警劉巖椅寺,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件浑槽,死亡現(xiàn)場離奇詭異,居然都是意外死亡返帕,警方通過查閱死者的電腦和手機桐玻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來荆萤,“玉大人镊靴,你說我怎么就攤上這事×淳拢” “怎么了偏竟?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長敞峭。 經(jīng)常有香客問我踊谋,道長,這世上最難降的妖魔是什么旋讹? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任殖蚕,我火速辦了婚禮,結(jié)果婚禮上沉迹,老公的妹妹穿的比我還像新娘睦疫。我一直安慰自己,他們只是感情好鞭呕,可當我...
    茶點故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布蛤育。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪缨伊。 梳的紋絲不亂的頭發(fā)上摘刑,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天,我揣著相機與錄音刻坊,去河邊找鬼枷恕。 笑死,一個胖子當著我的面吹牛谭胚,可吹牛的內(nèi)容都是我干的徐块。 我是一名探鬼主播,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼灾而,長吁一口氣:“原來是場噩夢啊……” “哼胡控!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起旁趟,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤昼激,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后锡搜,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體橙困,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年耕餐,在試婚紗的時候發(fā)現(xiàn)自己被綠了凡傅。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡肠缔,死狀恐怖夏跷,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情明未,我是刑警寧澤槽华,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站亚隅,受9級特大地震影響硼莽,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜煮纵,卻給世界環(huán)境...
    茶點故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望偏螺。 院中可真熱鬧行疏,春花似錦、人聲如沸套像。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至贞让,卻和暖如春周崭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背喳张。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工续镇, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留烁巫,地道東北人梦碗。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像铜邮,于是被迫代替她去往敵國和親舅桩。 傳聞我的和親對象是個殘疾皇子酱虎,可洞房花燭夜當晚...
    茶點故事閱讀 43,527評論 2 349

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn)擂涛,斷路器读串,智...
    卡卡羅2017閱讀 134,633評論 18 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,773評論 6 342
  • 什么是習慣? 1.每個習慣有三個組成部分:一個觸機(cue)撒妈,讓你的行動開展恢暖;一個跟該觸機直接相關聯(lián)的“獎勵”(r...
    鐵馬閱讀 732評論 0 1
  • 跑步時發(fā)現(xiàn),原來眼里有一個高樓踩身,朝著它一直跑胀茵,目的地最終會是它,不會跑偏挟阻。倘若沒有這小小的目標琼娘,難免跑偏,甚至漸行...
    木可莎閱讀 159評論 0 0
  • 由金凱瑞所飾演的喬爾和克萊門蒂娜相識相愛熄浓,可是不幸的是克萊門蒂娜因為霍華德博士的實驗失憶了,喬爾想盡各種辦法喚醒女...
    女生動漫閱讀 776評論 0 0