更多精彩文章。java
這麼多監控組件,總有一款適合你bash
《程序員畫像,十年沉浮》post
最有用系列:spa
《Linux生產環境上,最經常使用的一套「vim「技巧》線程
《Linux生產環境上,最經常使用的一套「Sed「技巧》code
若是你認同這些知識,歡迎關注微信公衆號小姐姐味道
ID:xjjdog
假設咱們有一個線程池,因爲程序須要,咱們向該線程池中提交了好多好多任務,可是 這些任務都沒有對異常進行try catch處理,而且運行的時候都拋出了異常 。這會對線程池的運行帶來什麼影響?
正確答案是:沒有影響。 這可不是好事情。
想一下,若是是你開發了一個線程池供開發者使用,你會不會對這種狀況作處理?想一想也是確定的,否則你提供給別人使用的東西就是有問題的,欠考慮的。並且java線程池的主要開發人員是大名鼎鼎的Doug Lea,你以爲他開發的代碼怎麼會容許出現這種問題?
這個問題很棘手,由於它躺在角落裏,程序正常運行的時候,它並不會出來做祟。
接下來咱們來看一下java中的線程池是如何運行咱們提交的任務的,詳細流程比較複雜,這裏咱們不關注,咱們只關注任務執行的部分。java中的線程池用的是ThreadPoolExecutor,真正執行代碼的部分是runWorker方法:final void runWorker(Worker w)
能夠看到,程序會捕獲包括Error在內的全部異常,而且在程序最後,將出現過的異常和當前任務傳遞給afterExecute方法。
而ThreadPoolExecutor中的afterExecute方法是沒有任何實現的。
protected void afterExecute(Runnable r, Throwable t) { }
複製代碼
想象下ThreadPoolExecutor這種處理方式會有什麼問題?
這樣作可以保證咱們提交的任務拋出了異常不會影響其餘任務的執行,同時也不會對用來執行該任務的線程產生任何影響。
問題就在afterExecute方法上, 這個方法沒有作任何處理,因此若是咱們的任務拋出了異常,咱們也沒法馬上感知到。 即便感知到了,也沒法查看異常信息。
因此,做爲一名好的開發者,是不該該容許這種狀況出現的。
思路很簡單。
一、在提交的任務中將異常捕獲並處理,不拋給線程池。
二、異常拋給線程池,可是咱們要及時處理拋出的異常。
第一種思路很簡單,就是咱們提交任務的時候,將全部可能的異常都Catch住,而且本身處理。
說白了就是把業務邏輯都trycatch起來。 可是這種思路的缺點就是: 1)全部的不一樣任務類型都要trycatch,增長了代碼量。
2)不存在checkedexception的地方也須要都trycatch起來,代碼醜陋。
第二種思路就能夠避免上面的兩個問題。
第二種思路又有如下四種實現方式
自定義線程池,繼承ThreadPoolExecutor並複寫其afterExecute(Runnable r, Throwable t)方法。
實現Thread.UncaughtExceptionHandler接口,實現void uncaughtException(Thread t, Throwable e);方法,並將該handler傳遞給線程池的ThreadFactory
覆蓋其uncaughtException方法。(與第二種方式相似,由於ThreadGroup類自己就實現了Thread.UncaughtExceptionHandler接口)
尤爲注意:上面三種方式針對的都是經過execute(xx)的方式提交任務,若是你提交任務用的是submit()方法,那麼上面的三種方式都將不起做用,而應該使用下面的方式
若是提交任務的時候使用的方法是submit,那麼該方法將返回一個Future對象,全部的異常以及處理結果均可以經過future對象獲取。 採用Future模式,將返回結果以及異常放到Future中,在Future中處理
異常處理是java中很是重要的流程,可是線程池的默認操做,會使的這些內容被靜悄悄的忽略,這在某些狀況下是致命的。
文章探討了從用戶層面的代碼到線程池層面的各類改造方法,力求讓業務代碼更加健壯可控。