<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          面試官:線程池執(zhí)行過程中遇到異常會(huì)發(fā)生什么,怎樣處理?

          共 8070字,需瀏覽 17分鐘

           ·

          2021-10-21 12:50

          線程遇到未處理的異常就結(jié)束了

          這個(gè)好理解,當(dāng)線程出現(xiàn)未捕獲異常的時(shí)候就執(zhí)行不下去了,留給它的就是垃圾回收了。

          線程池中線程頻繁出現(xiàn)未捕獲異常

          當(dāng)線程池中線程頻繁出現(xiàn)未捕獲的異常,那線程的復(fù)用率就大大降低了,需要不斷地創(chuàng)建新線程。

          做個(gè)實(shí)驗(yàn):

          public?class?ThreadExecutor?{

          ?private?ThreadPoolExecutor?threadPoolExecutor?=?new?ThreadPoolExecutor(1,?1,?60,?TimeUnit.SECONDS,
          ???new?ArrayBlockingQueue<>(200),?new?ThreadFactoryBuilder().setNameFormat("customThread?%d").build());

          ?@Test
          ?public?void?test()?{
          ??IntStream.rangeClosed(1,?5).forEach(i?->?{
          ???try?{
          ????Thread.sleep(100);
          ???}?catch?(InterruptedException?e)?{
          ????e.printStackTrace();
          ???}
          ???threadPoolExecutor.execute(()?->?{
          ?????int?j?=?1/0;
          ??});});
          ?}
          }

          新建一個(gè)只有一個(gè)線程的線程池,每隔0.1s提交一個(gè)任務(wù),任務(wù)中是一個(gè)1/0的計(jì)算。

          Exception?in?thread?"customThread?0"?java.lang.ArithmeticException:?/?by?zero
          ?at?thread.ThreadExecutor.lambda$null$0(ThreadExecutor.java:25)
          ?at?java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
          ?at?java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
          ?at?java.lang.Thread.run(Thread.java:748)
          Exception?in?thread?"customThread?1"?java.lang.ArithmeticException:?/?by?zero
          ?at?thread.ThreadExecutor.lambda$null$0(ThreadExecutor.java:25)
          ?at?java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
          ?at?java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
          ?at?java.lang.Thread.run(Thread.java:748)
          Exception?in?thread?"customThread?2"?java.lang.ArithmeticException:?/?by?zero
          ?at?thread.ThreadExecutor.lambda$null$0(ThreadExecutor.java:25)
          ?at?java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
          ?at?java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
          ?at?java.lang.Thread.run(Thread.java:748)
          Exception?in?thread?"customThread?3"?java.lang.ArithmeticException:?/?by?zero
          ?at?thread.ThreadExecutor.lambda$null$0(ThreadExecutor.java:25)
          ?at?java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
          ?at?java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
          ?at?java.lang.Thread.run(Thread.java:748)
          Exception?in?thread?"customThread?4"?java.lang.ArithmeticException:?/?by?zero
          ?at?thread.ThreadExecutor.lambda$null$0(ThreadExecutor.java:25)
          ?at?java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
          ?at?java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
          ?at?java.lang.Thread.run(Thread.java:748)
          Exception?in?thread?"customThread?5"?java.lang.ArithmeticException:?/?by?zero
          ?at?thread.ThreadExecutor.lambda$null$0(ThreadExecutor.java:25)
          ?at?java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
          ?at?java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
          ?at?java.lang.Thread.run(Thread.java:748)

          可見每次執(zhí)行的線程都不一樣,之前的線程都沒有復(fù)用。原因是因?yàn)槌霈F(xiàn)了未捕獲的異常。

          我們把異常捕獲試試:

          public?class?ThreadExecutor?{

          ?private?ThreadPoolExecutor?threadPoolExecutor?=?new?ThreadPoolExecutor(1,?1,?60,?TimeUnit.SECONDS,
          ???new?ArrayBlockingQueue<>(200),?new?ThreadFactoryBuilder().setNameFormat("customThread?%d").build());

          ?@Test
          ?public?void?test()?{
          ??IntStream.rangeClosed(1,?5).forEach(i?->?{
          ???try?{
          ????Thread.sleep(100);
          ???}?catch?(InterruptedException?e)?{
          ????e.printStackTrace();
          ???}
          ???threadPoolExecutor.execute(()?->?{
          ????try?{
          ?????int?j?=?1?/?0;
          ????}?catch?(Exception?e)?{
          ?????System.out.println(Thread.currentThread().getName()?+"?"+?e.getMessage());
          ????}
          ???});
          ??});
          ?}
          }
          customThread?0?/?by?zero
          customThread?0?/?by?zero
          customThread?0?/?by?zero
          customThread?0?/?by?zero
          customThread?0?/?by?zero

          可見當(dāng)異常捕獲了,線程就可以復(fù)用了。另外, 多線程系列面試題和答案全部整理好了,微信搜索Java技術(shù)棧,在后臺(tái)發(fā)送:面試,可以在線閱讀。

          問題來了,我們的代碼中異常不可能全部捕獲

          如果要捕獲那些沒被業(yè)務(wù)代碼捕獲的異常,可以設(shè)置Thread類的uncaughtExceptionHandler屬性。這時(shí)使用ThreadFactoryBuilder會(huì)比較方便,ThreadFactoryBuilder是guava提供的ThreadFactory生成器。

          new?ThreadFactoryBuilder()
          .setNameFormat("customThread?%d")
          .setUncaughtExceptionHandler((t,?e)?->?System.out.println(t.getName()?+?"發(fā)生異常"?+?e.getCause()))
          .build()

          修改之后:

          public?class?ThreadExecutor?{

          ?private?static?ThreadPoolExecutor?threadPoolExecutor?=?new?ThreadPoolExecutor(1,?1,?60,?TimeUnit.SECONDS,
          ???new?ArrayBlockingQueue<>(200),
          ???new?ThreadFactoryBuilder()
          ?????.setNameFormat("customThread?%d")
          ?????.setUncaughtExceptionHandler((t,?e)?->?System.out.println("UncaughtExceptionHandler捕獲到:"?+?t.getName()?+?"發(fā)生異常"?+?e.getMessage()))
          ?????.build());

          ?@Test
          ?public?void?test()?{
          ??IntStream.rangeClosed(1,?5).forEach(i?->?{
          ???try?{
          ????Thread.sleep(100);
          ???}?catch?(InterruptedException?e)?{
          ????e.printStackTrace();
          ???}

          ???threadPoolExecutor.execute(()?->?{
          ????System.out.println("線程"?+?Thread.currentThread().getName()?+?"執(zhí)行");
          ????int?j?=?1?/?0;
          ???});
          ??});
          ?}
          }
          線程customThread?0執(zhí)行
          UncaughtExceptionHandler捕獲到:customThread 0發(fā)生異常/ by zero
          線程customThread?1執(zhí)行
          UncaughtExceptionHandler捕獲到:customThread 1發(fā)生異常/ by zero
          線程customThread?2執(zhí)行
          UncaughtExceptionHandler捕獲到:customThread 2發(fā)生異常/ by zero
          線程customThread?3執(zhí)行
          UncaughtExceptionHandler捕獲到:customThread 3發(fā)生異常/ by zero
          線程customThread?4執(zhí)行
          UncaughtExceptionHandler捕獲到:customThread 4發(fā)生異常/ by zero

          可見,結(jié)果并不是我們想象的那樣,線程池中原有的線程沒有復(fù)用!所以通過UncaughtExceptionHandler想將異常吞掉使線程復(fù)用這招貌似行不通。它只是做了一層異常的保底處理。

          推薦一個(gè) Spring Boot 基礎(chǔ)教程及實(shí)戰(zhàn)示例:https://www.javastack.cn/categories/Spring-Boot/

          將excute改成submit試試

          public?class?ThreadExecutor?{

          ?private?static?ThreadPoolExecutor?threadPoolExecutor?=?new?ThreadPoolExecutor(1,?1,?60,?TimeUnit.SECONDS,
          ???new?ArrayBlockingQueue<>(200),
          ???new?ThreadFactoryBuilder()
          ?????.setNameFormat("customThread?%d")
          ?????.setUncaughtExceptionHandler((t,?e)?->?System.out.println("UncaughtExceptionHandler捕獲到:"?+?t.getName()?+?"發(fā)生異常"?+?e.getMessage()))
          ?????.build());

          ?@Test
          ?public?void?test()?{
          ??IntStream.rangeClosed(1,?5).forEach(i?->?{
          ???try?{
          ????Thread.sleep(100);
          ???}?catch?(InterruptedException?e)?{
          ????e.printStackTrace();
          ???}

          ???Future?future?=?threadPoolExecutor.submit(()?->?{
          ????System.out.println("線程"?+?Thread.currentThread().getName()?+?"執(zhí)行");
          ????int?j?=?1?/?0;
          ???});
          ???try?{
          ????future.get();
          ???}?catch?(InterruptedException?e)?{
          ????e.printStackTrace();
          ???}?catch?(ExecutionException?e)?{
          ????e.printStackTrace();
          ???}
          ??});
          ?}
          }
          線程customThread?0執(zhí)行
          java.util.concurrent.ExecutionException:?java.lang.ArithmeticException:?/?by?zero
          線程customThread?0執(zhí)行
          java.util.concurrent.ExecutionException:?java.lang.ArithmeticException:?/?by?zero
          線程customThread?0執(zhí)行
          java.util.concurrent.ExecutionException:?java.lang.ArithmeticException:?/?by?zero
          線程customThread?0執(zhí)行
          java.util.concurrent.ExecutionException:?java.lang.ArithmeticException:?/?by?zero
          線程customThread?0執(zhí)行
          java.util.concurrent.ExecutionException:?java.lang.ArithmeticException:?/?by?zero

          通過submit提交線程可以屏蔽線程中產(chǎn)生的異常,達(dá)到線程復(fù)用。當(dāng)get()執(zhí)行結(jié)果時(shí)異常才會(huì)拋出。

          原因是通過submit提交的線程,當(dāng)發(fā)生異常時(shí),會(huì)將異常保存,待future.get();時(shí)才會(huì)拋出。最新面試題整理好了,點(diǎn)擊Java面試庫小程序在線刷題。

          這是Futuretask的部分run()方法,看setException:

          public?void?run()?{
          ????try?{
          ????????Callable?c?=?callable;
          ????????if?(c?!=?null?&&?state?==?NEW)?{
          ????????????V?result;
          ????????????boolean?ran;
          ????????????try?{
          ????????????????result?=?c.call();
          ????????????????ran?=?true;
          ????????????}?catch?(Throwable?ex)?{
          ????????????????result?=?null;
          ????????????????ran?=?false;
          ????????????????setException(ex);
          ????????????}
          ????????????if?(ran)
          ????????????????set(result);
          ????????}
          ????}?
          }

          protected?void?setException(Throwable?t)?{
          ????if?(UNSAFE.compareAndSwapInt(this,?stateOffset,?NEW,?COMPLETING))?{
          ????????outcome?=?t;
          ????????UNSAFE.putOrderedInt(this,?stateOffset,?EXCEPTIONAL);?//?final?state
          ????????finishCompletion();
          ????}
          }

          將異常存在outcome對(duì)象中,沒有拋出,再看get方法:

          public?V?get()?throws?InterruptedException,?ExecutionException?{
          ????int?s?=?state;
          ????if?(s?<=?COMPLETING)
          ????????s?=?awaitDone(false,?0L);
          ????return?report(s);
          }
          private?V?report(int?s)?throws?ExecutionException?{
          ????Object?x?=?outcome;
          ????if?(s?==?NORMAL)
          ????????return?(V)x;
          ????if?(s?>=?CANCELLED)
          ????????throw?new?CancellationException();
          ????throw?new?ExecutionException((Throwable)x);
          }

          當(dāng)outcome是異常時(shí)才拋出。

          總結(jié)

          1、線程池中線程中異常盡量手動(dòng)捕獲

          2、通過設(shè)置ThreadFactory的UncaughtExceptionHandler可以對(duì)未捕獲的異常做保底處理,通過execute提交任務(wù),線程依然會(huì)中斷,而通過submit提交任務(wù),可以獲取線程執(zhí)行結(jié)果,線程異常會(huì)在get執(zhí)行結(jié)果時(shí)拋出。

          本文鏈接:https://blog.csdn.net/weixin_37968613/article/details/108407774

          版權(quán)聲明:本文為博主原創(chuàng)文章,遵循 CC 4.0 BY-SA 版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接和本聲明。

          —?【 THE END 】—
          本公眾號(hào)全部博文已整理成一個(gè)目錄,請(qǐng)?jiān)诠娞?hào)里回復(fù)「m」獲取!

          最近面試BAT,整理一份面試資料Java面試BATJ通關(guān)手冊(cè),覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫、數(shù)據(jù)結(jié)構(gòu)等等。

          獲取方式:點(diǎn)“在看”,關(guān)注公眾號(hào)并回復(fù) PDF?領(lǐng)取,更多內(nèi)容陸續(xù)奉上。

          文章有幫助的話,在看,轉(zhuǎn)發(fā)吧。

          謝謝支持喲 (*^__^*)

          瀏覽 39
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  蜜桃人妻| 色哟哟——国产精品 | 久久午夜福利视频 | 91 国产 爽 黄 喷水 | 一级婬片试看15分钟免费 |