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