java异常_Java线程池「异常处理」正确姿势:有病就得治
假設我們有一個線程池,由于程序需要,我們向該線程池中提交了好多好多任務,但是 這些任務都沒有對異常進行try catch處理,并且運行的時候都拋出了異常 。這會對線程池的運行帶來什么影響?
正確答案是:沒有影響。這可不是好事情。
想一下,如果是你開發了一個線程池供開發者使用,你會不會對這種情況做處理?想想也是肯定的,不然你提供給別人使用的東西就是有問題的,欠考慮的。而且java線程池的主要開發人員是大名鼎鼎的Doug Lea,你覺得他開發的代碼怎么會允許出現這種問題?
這個問題很棘手,因為它躺在角落里,程序正常運行的時候,它并不會出來作祟。
問題分析
接下來我們來看一下java中的線程池是如何運行我們提交的任務的,詳細流程比較復雜,這里我們不關注,我們只關注任務執行的部分。java中的線程池用的是ThreadPoolExecutor,真正執行代碼的部分是runWorker方法:final void runWorker(Worker w)
可以看到,程序會捕獲包括Error在內的所有異常,并且在程序最后,將出現過的異常和當前任務傳遞給afterExecute方法。
而ThreadPoolExecutor中的afterExecute方法是沒有任何實現的。
protected存在問題
想象下ThreadPoolExecutor這種處理方式會有什么問題?
這樣做能夠保證我們提交的任務拋出了異常不會影響其他任務的執行,同時也不會對用來執行該任務的線程產生任何影響。
問題就在afterExecute方法上, 這個方法沒有做任何處理,所以如果我們的任務拋出了異常,我們也無法立刻感知到。 即使感知到了,也無法查看異常信息。
所以,作為一名好的開發者,是不應該允許這種情況出現的。
如何避免這種問題
思路很簡單。
1、在提交的任務中將異常捕獲并處理,不拋給線程池。
2、異常拋給線程池,但是我們要及時處理拋出的異常。
直接catch
第一種思路很簡單,就是我們提交任務的時候,將所有可能的異常都Catch住,并且自己處理。
說白了就是把業務邏輯都trycatch起來。
但是這種思路的缺點就是:
1)所有的不同任務類型都要trycatch,增加了代碼量。
2)不存在checkedexception的地方也需要都trycatch起來,代碼丑陋。
線程池實現
第二種思路就可以避免上面的兩個問題。
第二種思路又有以下四種實現方式
自定義線程池
自定義線程池,繼承ThreadPoolExecutor并復寫其afterExecute(Runnable r, Throwable t)方法。
實現Thread.UncaughtExceptionHandler接口
實現Thread.UncaughtExceptionHandler接口,
實現void uncaughtException(Thread t, Throwable e);方法,
并將該handler傳遞給線程池的ThreadFactory
繼承ThreadGroup
覆蓋其uncaughtException方法。(與第二種方式類似,因為ThreadGroup類本身就實現了Thread.UncaughtExceptionHandler接口)
尤其注意:上面三種方式針對的都是通過execute(xx)的方式提交任務,如果你提交任務用的是submit()方法,那么上面的三種方式都將不起作用,而應該使用下面的方式采用Future模式
如果提交任務的時候使用的方法是submit,那么該方法將返回一個Future對象,所有的異常以及處理結果都可以通過future對象獲取。
采用Future模式,將返回結果以及異常放到Future中,在Future中處理
總結
文章探討了從用戶層面的代碼到線程池層面的各種改造方法,力求讓業務代碼更加健壯可控。異常處理是java中非常重要的流程,但是線程池的默認操作,會使的這些內容被靜悄悄的忽略,這在某些情況下是致命的。
原作者:sanshao原文鏈接:Java線程池執行的任務拋出異常看不到日志詳解_編程語言_IT蝦米網
原出處:蝦米網
總結
以上是生活随笔為你收集整理的java异常_Java线程池「异常处理」正确姿势:有病就得治的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: msi怎么设置f盘启动 微星电脑设置F盘
- 下一篇: bios怎么改ide模式 BIOS如何修