日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

Java多线程:捕获线程异常

發(fā)布時(shí)間:2025/3/21 java 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java多线程:捕获线程异常 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

你處理過多線程中的異常嗎?如何捕獲多線程中發(fā)生的異常?捕獲子線程的異常與捕獲當(dāng)前線程的異常一樣簡單嗎?

除了try catch。Java中還可以通過異常處理器UncaughtExceptionHandler來處理那些未捕獲的異常。

# 在當(dāng)前線程捕獲當(dāng)前線程發(fā)生的異常:

/***?@author?futao*?@date?2020/6/17*/ @Slf4j public?class?ExceptionInCurThread?{public?static?void?main(String[]?args)?{try?{throw?new?RuntimeException("在主線程拋出異常,在主線程捕獲");}?catch?(RuntimeException?e)?{log.error("捕獲到異常",?e);}} }
  • 結(jié)果:

  • 結(jié)論:在當(dāng)前線程通過try catch可以捕獲當(dāng)前線程拋出的異常。

# 可以在當(dāng)前通過try catch的方式捕獲其他線程拋出的異常嗎?'

/***?@author?喜歡天文的pony站長*?Created?on?2020/6/16.*/ public?class?ExceptionInChildThread?implements?Runnable?{@Overridepublic?void?run()?{throw?new?RuntimeException("子線程發(fā)生了異常...");}/***?模擬子線程發(fā)生異常**?@throws?InterruptedException*/private?static?void?exceptionThread()?throws?InterruptedException?{new?Thread(new?ExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);new?Thread(new?ExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);new?Thread(new?ExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);new?Thread(new?ExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);}/***?在主線程嘗試通過try?catch捕獲異常*/private?static?void?catchInMain()?{try?{exceptionThread();}?catch?(Exception?e)?{//無法捕獲發(fā)生在其他線程中的異常log.error("捕獲到了異常?",?e);}}public?static?void?main(String[]?args)?throws?InterruptedException?{ExceptionInChildThread.catchInMain();}}
  • (錯(cuò)誤的)預(yù)期:

    • 在運(yùn)行第一個(gè)線程的時(shí)候發(fā)生了異常,被catch捕獲,打印捕獲到了異常?和異常堆棧且后面的線程將不會(huì)運(yùn)行。

  • 實(shí)際運(yùn)行結(jié)果:

    • 并不符合預(yù)期。

    • 沒有被try catch捕獲。

    • 后續(xù)的線程沒有因?yàn)榈谝粋€(gè)線程發(fā)生異常而跳過。

  • 結(jié)論:

    • 無法在一個(gè)線程中通過try catch捕獲另外一個(gè)線程的異常。

# 解決方案

  • 在每個(gè)線程內(nèi)部run()方法內(nèi)通過try catch捕獲當(dāng)前線程發(fā)生的異常。

    • 缺點(diǎn):每個(gè)線程都需要編寫重復(fù)的try catch 代碼

  • 使用線程異常處理器UncaughtExceptionHandler

    • 給所有線程設(shè)置統(tǒng)一的異常處理器

    • 給每個(gè)線程設(shè)置特定的異常處理器

    • 給線程組設(shè)置異常處理器

    • 給線程池設(shè)置異常處理器

      • 因?yàn)榫€程池也是通過new Thread()的方式創(chuàng)建的線程,所以思想與上面兩種方法一致。

      • 注意:execute()與submit()方式對????異常處理的不同。

  • # 在線程內(nèi)部run()通過try catch捕獲異常

    /***?@author?喜歡天文的pony站長*?Created?on?2020/6/16.*/ @Slf4j public?class?ExceptionInChildThread?implements?Runnable?{@Overridepublic?void?run()?{try?{//do?something?else...throw?new?RuntimeException("子線程發(fā)生了異常...");}?catch?(Exception?e)?{log.error("在線程內(nèi)部捕獲異常",?e);}}/***?模擬子線程發(fā)生異常**?@throws?InterruptedException*/private?static?void?exceptionThread()?throws?InterruptedException?{new?Thread(new?ExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);new?Thread(new?ExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);new?Thread(new?ExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);new?Thread(new?ExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);}/***?在主線程嘗試通過try?catch捕獲異常*/private?static?void?catchInMain()?{try?{exceptionThread();}?catch?(Exception?e)?{//無法捕獲發(fā)生在其他線程中的異常log.error("捕獲到了異常?",?e);}}public?static?void?main(String[]?args)?throws?InterruptedException?{ExceptionInChildThread.catchInMain();} }
    • 結(jié)果:

      • 成功在子線程內(nèi)部run()方法捕獲到了異常

    # 使用線程異常處理器UncaughtExceptionHandler

    當(dāng)一個(gè)線程由于未捕獲異常而退出時(shí),JVM會(huì)把這個(gè)事件報(bào)告給應(yīng)用程序提供的UncaughtExceptionHandler異常處理器

    • 自定義線程異常處理器

    /***?自定義線程未捕獲異常處理器**?@author?futao*?@date?2020/6/17*/ public?class?CustomThreadUncaughtExceptionHandler?implements?Thread.UncaughtExceptionHandler?{private?static?final?Logger?LOGGER?=?LoggerFactory.getLogger(CustomThreadUncaughtExceptionHandler.class);@Overridepublic?void?uncaughtException(Thread?t,?Throwable?e)?{LOGGER.error("捕獲到線程發(fā)生的異常,線程信息:[{}]",?JSON.toJSONString(t),?e);} }
    • 使用:

    1. 全局:

    • Thread.setDefaultUncaughtExceptionHandler(new CustomThreadUncaughtExceptionHandler());

      • 通過調(diào)用Thread的靜態(tài)方法setDefaultUncaughtExceptionHandler(),設(shè)置Thread的靜態(tài)屬性defaultUncaughtExceptionHandler.為我們自定義的異常處理器。

      • 源碼:

    測試:

    /***?@author?喜歡天文的pony站長*?Created?on?2020/6/16.*/ @Slf4j public?class?ExceptionInChildThread?implements?Runnable?{@Overridepublic?void?run()?{throw?new?RuntimeException("子線程發(fā)生了異常...");}/***?模擬子線程發(fā)生異常**?@throws?InterruptedException*/private?static?void?exceptionThread()?throws?InterruptedException?{new?Thread(new?ExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);new?Thread(new?ExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);new?Thread(new?ExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);new?Thread(new?ExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);}public?static?void?main(String[]?args)?throws?InterruptedException?{//設(shè)置全局的線程異常處理器Thread.setDefaultUncaughtExceptionHandler(new?CustomThreadUncaughtExceptionHandler());exceptionThread();} }
    • 結(jié)果: 成功捕獲

    2. 為指定線程設(shè)置特定的異常處理器

    • 細(xì)心的同學(xué)已經(jīng)發(fā)現(xiàn)了,在上面Thread類的截圖中,還有一個(gè)實(shí)例屬性private volatile UncaughtExceptionHandler uncaughtExceptionHandler;。通過給這個(gè)屬性賦值,可以實(shí)現(xiàn)為每個(gè)線程對象設(shè)置不同的異常處理器。

    • 測試使用

    /***?@author?喜歡天文的pony站長*?Created?on?2020/6/16.*/ @Slf4j public?class?ExceptionInChildThread?implements?Runnable?{@Overridepublic?void?run()?{throw?new?RuntimeException("子線程發(fā)生了異常...");}/***?模擬子線程發(fā)生異常**?@throws?InterruptedException*/private?static?void?exceptionThread()?throws?InterruptedException?{Thread?thread1?=?new?Thread(new?ExceptionInChildThread());//為指定線程設(shè)置特定的異常處理器thread1.setUncaughtExceptionHandler(new?CustomThreadUncaughtExceptionHandler());thread1.start();TimeUnit.MILLISECONDS.sleep(200L);new?Thread(new?ExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);new?Thread(new?ExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);new?Thread(new?ExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);}public?static?void?main(String[]?args)?throws?InterruptedException?{exceptionThread();} }
    • 結(jié)果: 成功捕獲線程1的異常信息

    3. 線程組

    /***?@author?futao*?@date?2020/6/20*/ @Slf4j public?class?ExceptionInThreadGroup?implements?Runnable?{@Overridepublic?void?run()?{throw?new?RuntimeException("線程任務(wù)發(fā)生了異常");}public?static?void?main(String[]?args)?throws?InterruptedException?{ThreadGroup?threadGroup?=?new?ThreadGroup("只知道拋出異常的線程組...")?{@Overridepublic?void?uncaughtException(Thread?t,?Throwable?e)?{super.uncaughtException(t,?e);log.error("線程組內(nèi)捕獲到線程[{},{}]異常",?t.getId(),?t.getName(),?e);}};ExceptionInThreadGroup?exceptionInThreadGroup?=?new?ExceptionInThreadGroup();new?Thread(threadGroup,?exceptionInThreadGroup,?"線程1").start();TimeUnit.MILLISECONDS.sleep(300L);//優(yōu)先獲取綁定在thread對象上的異常處理器Thread?thread?=?new?Thread(threadGroup,?exceptionInThreadGroup,?"線程2");thread.setUncaughtExceptionHandler(new?CustomThreadUncaughtExceptionHandler());thread.start();TimeUnit.MILLISECONDS.sleep(300L);new?Thread(threadGroup,?exceptionInThreadGroup,?"線程3").start();} }
    • 結(jié)果:

    4. 線程池

    /***?@author?futao*?@date?2020/6/17*/ public?class?CatchThreadPoolException?{public?static?void?main(String[]?args)?{ThreadPoolExecutor?threadPoolExecutor?=?new?ThreadPoolExecutor(2,4,1L,TimeUnit.MINUTES,new?LinkedBlockingDeque<>(1024),new?ThreadFactory()?{@Overridepublic?Thread?newThread(Runnable?r)?{Thread?thread?=?new?Thread(r);//設(shè)置線程異常處理器thread.setUncaughtExceptionHandler(new?CustomThreadUncaughtExceptionHandler());return?thread;}});threadPoolExecutor.execute(new?Runnable()?{@Overridepublic?void?run()?{throw?new?RuntimeException("execute()發(fā)生異常");}});threadPoolExecutor.submit(new?Runnable()?{@Overridepublic?void?run()?{throw?new?RuntimeException("submit.run()發(fā)生異常");}});threadPoolExecutor.submit(new?Callable<String>()?{@Overridepublic?String?call()?throws?Exception?{throw?new?RuntimeException("submit.call()發(fā)生異常");}});threadPoolExecutor.shutdown();} }
    • 結(jié)果: 并不符合預(yù)期,預(yù)期應(yīng)該捕獲三個(gè)異常

      • 只捕獲到了通過execute()提交的任務(wù)的異常

      • 沒有捕獲到通過submit()提交的任務(wù)的異常

    • 通過afterExecute()捕獲submit()任務(wù)的異常

      • 通過submit()方法的源碼可以發(fā)現(xiàn),submit()是將runnable()封裝成了RunnableFuture<Void>,并最終調(diào)用execute(ftask);執(zhí)行。

    /***?@author?futao*?@date?2020/6/17*/ @Slf4j public?class?CatchThreadPoolException?{public?static?void?main(String[]?args)?throws?InterruptedException,?ExecutionException?{ThreadPoolExecutor?threadPoolExecutor?=?new?ThreadPoolExecutor(2,4,1L,TimeUnit.MINUTES,new?LinkedBlockingDeque<>(1024),new?ThreadFactory()?{@Overridepublic?Thread?newThread(Runnable?r)?{Thread?thread?=?new?Thread(r);//設(shè)置線程異常處理器thread.setUncaughtExceptionHandler(new?CustomThreadUncaughtExceptionHandler());return?thread;}})?{/***?捕獲{@code?FutureTask<?>}拋出的異常**?@param?r*?@param?t*/@Overrideprotected?void?afterExecute(Runnable?r,?Throwable?t)?{super.afterExecute(r,?t);if?(r?instanceof?FutureTask<?>)?{try?{//get()的時(shí)候會(huì)將異常內(nèi)的異常拋出((FutureTask<?>)?r).get();}?catch?(InterruptedException?e)?{e.printStackTrace();Thread.currentThread().interrupt();}?catch?(ExecutionException?e)?{log.error("捕獲到線程的異常返回值",?e);}}//Throwable?t永遠(yuǎn)為null,拿不到異常信息//log.error("afterExecute中捕獲到異常,", t);}};threadPoolExecutor.execute(new?Runnable()?{@Overridepublic?void?run()?{throw?new?RuntimeException("execute()發(fā)生異常");}});TimeUnit.MILLISECONDS.sleep(200L);threadPoolExecutor.submit(new?Runnable()?{@Overridepublic?void?run()?{throw?new?RuntimeException("submit.run()發(fā)生異常");}});TimeUnit.MILLISECONDS.sleep(200L);threadPoolExecutor.submit(new?Callable<String>()?{@Overridepublic?String?call()?throws?Exception?{throw?new?RuntimeException("submit.call()發(fā)生異常");}}).get();???//get()的時(shí)候會(huì)將異常拋出threadPoolExecutor.shutdown();} }

    歡迎在評論區(qū)留下你看文章時(shí)的思考,及時(shí)說出,有助于加深記憶和理解,還能和像你一樣也喜歡這個(gè)話題的讀者相遇~

    # 本文源代碼

    • https://github.com/FutaoSmile/learn-thread/tree/master/src/main/java/com/futao/learn/threads/捕獲線程異常

    總結(jié)

    以上是生活随笔為你收集整理的Java多线程:捕获线程异常的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。