ThreadPoolExecutor參數(shù)詳解和優(yōu)化建議

        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
            5,
            10,
            20,
            TimeUnit.MINUTES,
            new SynchronousQueue<Runnable>(),
            new ThreadFactory() {
                @Override
                public Thread newThread(Runnable r) {
                    return new Thread();
                }
            },
            new RejectedExecutionHandler() {
                @Override
                public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {

                }
            });

按照上面參數(shù)順序講解

  1. \color{#FF0000}{corePoolSize 核心線程池數(shù)量 }
  • 創(chuàng)建線程池的時候沒有線程, 當提交任務的時候會陸續(xù)創(chuàng)建線程, 當corePoolSize 滿的時候, 會將任務放到隊列中去, 隊列滿了, 那么會繼續(xù)創(chuàng)建 corePoolIsze 到maxPoolSize之間的線程 .
  • 設置allowCoreThreadTimeout=true(默認false)時镣陕,核心線程會超時關閉
  • prestartAllCoreThread 或者prestartAllCoreThread 初始化核心線程
  1. \color{#FF0000}{maxPoolSize最大線程池數(shù)量}
  • 當線程數(shù)量 = maxPoolSize , 且任務隊列已經(jīng)滿了, 線程池會拒絕任務拋出一場
  • 我們的項目 這個參數(shù)是 Runtime.getRuntime().availableProcessors() * 4 +1 , 根據(jù)壓力測試獲得最好的最大核心數(shù)量
  1. \color{#FF0000}{keepAliveTime 線程空閑時間}
  • 空閑的線程能夠保持空閑的時間, 超過這個時間, 這一部分線程將被回收.
  • 核心線程到最大線程數(shù)量的差異, 如果兩個值相等, 那么這個參數(shù)毫無意義.
  1. \color{#FF0000}{BlockingQueue 阻塞隊列}
  • 當核心線程數(shù)達到最大時,新任務會放在隊列中排隊等待執(zhí)行
  • 常見的隊列
    1. ArrayBlockingQueue: 有界隊列愉择,基于數(shù)組結構严衬,按照隊列FIFO原則對元素排序;
    2. LinkedBlockingQueue:無界隊列抚笔,基于鏈表結構扶认,按照隊列FIFO原則對元素排序,Executors.newFixedThreadPool()使用了這個隊列
    3. SynchronousQueue 同步隊列, 該隊列不存儲元素殊橙,每個插入操作必須等待另一個線程調(diào)用移除操作辐宾,否則插入操作會一直被阻塞,Executors.newCachedThreadPool()使用了這個隊列膨蛮;
    4. PriorityBlockingQueue:優(yōu)先級隊列叠纹,具有優(yōu)先級的無限阻塞隊列。

6 . \color{#FF0000}{ThreadFactory 線程工廠}

  • 自定義線程的名字,daemon,優(yōu)先級等
  1. \color{#FF0000}{rejectedExecutionHandler 拒絕策略}
  • 這里的拒絕可以采取你自定義的辦法, 比如使用second 線程池處理 r , 或者丟棄r, 或者打印出日志, 取決于你的業(yè)務.
  • 線程池執(zhí)行拒絕的兩種情況
    1. 當線程數(shù)量達到maxPoolSize , 隊列已經(jīng)滿了, 會拒絕新任務
    2. 當線程池調(diào)用shutdown 的時候, 會等待線程池的任務執(zhí)行完畢, 再shutdown, 這是一種優(yōu)雅的方式. 調(diào)用shutdown和shutdown關閉之前, 這段時間會拒絕新任務. shutdownNow, 會立刻關閉, 并且停止執(zhí)行任務. 和shutdown 有很大區(qū)別.
  • 幾種拒絕策略
    1. AbortPolicy 丟棄任務, 拋運行異常(如果不設置, 默認就是這一個策略)
    2. CallerRunsPolicy 執(zhí)行任務
    3. DiscardPolicy 忽視鸽疾,什么都不會發(fā)生
    4. DiscardOldestPolicy 從隊列中踢出最先進入隊列(最后一個執(zhí)行)的任務

線程執(zhí)行的一些流程

  1. 如果當前線程池中的線程數(shù)目 小于 < corePoolSize 吊洼。則對于每個新任務,都會創(chuàng)建一個線程去執(zhí)行這個任務(即使core線程中也有空閑線程, 也會新創(chuàng)建線程會執(zhí)行)制肮。
  2. 如果當前線程池中的線程數(shù)目 大于等于 >= corePoolSize 冒窍。
    對于每個新任務,會將其添加到任務隊列中豺鼻。若添加成功综液,則任務由空閑的線程主動將其從隊列中移除后執(zhí)行。若添加失斎屐(任務隊列已滿)谬莹,則嘗試創(chuàng)建新的線程執(zhí)行。
  3. 若當前線程池中的線程數(shù)目到達 maximumPoolSize 桩了,則對于新任務采取拒絕策略附帽。
  4. 如果線程池中的數(shù)量大于 corePoolSize 時,如果某個線程空閑時間超過 keepAliveTime 井誉,線程會被終止蕉扮,直到線程池中的線程數(shù)目不超過 corePoolSize 。
  5. 如果為核心線程池中的線程設置存活時間颗圣,則當核心線程池中的線程空閑時間超過 keepAliveTime 喳钟,則該線程也會被終止
  6. 如果核心線程數(shù)已經(jīng)達到, 如果沒有隊列沒有滿的話, 是不會創(chuàng)建新的線程. 有時候取決你使用什么隊列. 比如使用 ArrayBlockingQueue(10), 當核心線程已經(jīng)創(chuàng)建完成, 只有當隊列滿了之后才會繼續(xù)創(chuàng)建新的線程. 如果你使用的是SynchronousQueue, 內(nèi)部只能一個的隊列,那么只有隊列直接創(chuàng)建core線程到maxpoolSize 之間的線程

線程池性能優(yōu)化建議

  1. 建議使用 prestartAllCoreThread 或者prestartAllCoreThread 初始化核心線程
  2. 考慮 allowCoreThreadTimeOut 允許核心線程能夠回收節(jié)約機器的使用.
  3. 拒絕策略可以將任務, 提交給 second 線程池處理.
  4. 自定義ThreadFactory ,標識線程, 方便排查線程的使用情況

關于線程異常是否處理的問題:

  1. execute : 如果不手動捕獲一場, 線程池只能重新創(chuàng)建新的異常來填補空白屁使,重新創(chuàng)建線程這是有代價的
  2. submit: 因為能夠調(diào)用 future.get(). 所以有異常也會捕獲, 不會造成線程終止.
// 證明execute 的異常
    @Test
    public void test1() throws InterruptedException {
        Thread.setDefaultUncaughtExceptionHandler((Thread t, Throwable e) -> {
            log.warn("Exception in thread {}", t, e);
        });
        String prefix = "test";
        ExecutorService threadPool = Executors.newFixedThreadPool(1, new ThreadUtil.ThreadFactoryImpl(prefix));
        IntStream.rangeClosed(1, 10).forEach(i -> threadPool.execute(() -> {
            if (i == 5) {
                throw new RuntimeException("error");
            }
            log.info("I'm done : {}", i);
            System.out.println(Thread.currentThread().getName() + " I'm done : " + i);
            if (i < 5) {
                Assert.assertEquals(prefix + "1",  Thread.currentThread().getName());
            } else {
                Assert.assertEquals(prefix + "2", Thread.currentThread().getName());
            }

        }));
        threadPool.shutdown();
        threadPool.awaitTermination(1, TimeUnit.HOURS);
        // 本來是通過test 1 線程執(zhí)行的, 后面出現(xiàn)異常 確是test2 執(zhí)行的, 說明x線程已經(jīng)終止2, 并且重新創(chuàng)建線程
    }
// 線程名稱沒有變, 說明已經(jīng)幫你捕獲異常.
String prefix = "test";
        ExecutorService threadPool = Executors.newFixedThreadPool(1, new ThreadUtil.ThreadFactoryImpl(prefix));
        List<Future> futures = new ArrayList<>();
        IntStream.rangeClosed(1, 10).forEach(i -> futures.add(threadPool.submit(() -> {
            if (i == 5) {
                throw new RuntimeException("error");
            }
            log.info("I'm done : {}", i);
//            if (i < 5) Assert.assertEquals(prefix + "1", Thread.currentThread().getName());
//            else Assert.assertEquals(prefix + "2", Thread.currentThread().getName());
        })));

        for (Future future : futures) {
            try {
                future.get();
            } catch (ExecutionException e) {
                log.warn("future ExecutionException", e);
            }
        }
        threadPool.shutdown();
        threadPool.awaitTermination(1, TimeUnit.HOURS);

面試問題

  1. 說說線程池的核心參數(shù)有哪些
  2. 說說你們的corePoolSize 的數(shù)量是如何設置,超時時間如何設置
  3. 你們使用的是什么隊列, 為什么使用這個隊列.
  4. 你們項目是如何優(yōu)化自己的線程池參數(shù)的.
  5. 當線程池還沒達到 corePoolSize 的時候, 線程池里面有空閑線程, 這個時候來了一個新的任務, 線程池是創(chuàng)建新的線程還是使用空閑線程 ?

路過點贊, 月入10w

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市奔则,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌酬蹋,老刑警劉巖除嘹,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件璃岳,死亡現(xiàn)場離奇詭異,居然都是意外死亡犁柜,警方通過查閱死者的電腦和手機馋缅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進店門皆的,熙熙樓的掌柜王于貴愁眉苦臉地迎上來费薄,“玉大人伟众,你說我怎么就攤上這事噪径≌野” “怎么了?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵眼俊,是天一觀的道長疮胖。 經(jīng)常有香客問我院塞,道長拦止,這世上最難降的妖魔是什么汹族? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任搁拙,我火速辦了婚禮,結果婚禮上朋譬,老公的妹妹穿的比我還像新娘盐茎。我一直安慰自己,他們只是感情好徙赢,可當我...
    茶點故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布字柠。 她就那樣靜靜地躺著探越,像睡著了一般。 火紅的嫁衣襯著肌膚如雪窑业。 梳的紋絲不亂的頭發(fā)上钦幔,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天,我揣著相機與錄音常柄,去河邊找鬼鲤氢。 笑死,一個胖子當著我的面吹牛西潘,可吹牛的內(nèi)容都是我干的卷玉。 我是一名探鬼主播,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼喷市,長吁一口氣:“原來是場噩夢啊……” “哼相种!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起品姓,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤蚂子,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后缭黔,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡蒂破,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年馏谨,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片附迷。...
    茶點故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡惧互,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出喇伯,到底是詐尸還是另有隱情喊儡,我是刑警寧澤,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布稻据,位于F島的核電站艾猜,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏捻悯。R本人自食惡果不足惜匆赃,卻給世界環(huán)境...
    茶點故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望今缚。 院中可真熱鬧算柳,春花似錦、人聲如沸姓言。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至囱淋,卻和暖如春猪杭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背绎橘。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工胁孙, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人称鳞。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓涮较,卻偏偏與公主長得像,于是被迫代替她去往敵國和親冈止。 傳聞我的和親對象是個殘疾皇子狂票,可洞房花燭夜當晚...
    茶點故事閱讀 43,446評論 2 348