福哥答案2020-06-04:
福哥口訣法:拒中丟老調(線程池拒絕策略:中止策略、丟棄策略、棄老策略、調用者運行策略)
簡單回答:
中止策略:無特殊場景。
丟棄策略:無關緊要的任務(博客閱讀量)。
棄老策略:發布消息。
調用者運行策略:不允許失敗場景(對性能要求不高、並發量較小)。
詳細回答:
1.AbortPolicy中止策略:丟棄任務並拋出RejectedExecutionException異常。
這是線程池默認的拒絕策略,在任務不能再提交的時候,拋出異常,及時反饋程序運行狀態。如果是比較關鍵的業務,推薦使用此拒絕策略,這樣子在系統不能承載更大的並發量的時候,能夠及時的通過異常發現。
功能:當觸發拒絕策略時,直接拋出拒絕執行的異常,中止策略的意思也就是打斷當前執行流程.
使用場景:這個就沒有特殊的場景了,但是有一點要正確處理拋出的異常。ThreadPoolExecutor中默認的策略就是AbortPolicy,ExecutorService接口的系列ThreadPoolExecutor因為都沒有顯示的設置拒絕策略,所以默認的都是這個。但是請注意,ExecutorService中的線程池實例隊列都是無界的,也就是說把內存撐爆了都不會觸發拒絕策略。當自己自定義線程池實例時,使用這個策略一定要處理好觸發策略時拋的異常,因為他會打斷當前的執行流程。
2.DiscardPolicy丟棄策略:ThreadPoolExecutor.DiscardPolicy:丟棄任務,但是不拋出異常。如果線程隊列已滿,則后續提交的任務都會被丟棄,且是靜默丟棄。
使用此策略,可能會使我們無法發現系統的異常狀態。建議是一些無關緊要的業務采用此策略。例如,本人的博客網站統計閱讀量就是采用的這種拒絕策略。
功能:直接靜悄悄的丟棄這個任務,不觸發任何動作。
使用場景:如果你提交的任務無關緊要,你就可以使用它 。因為它就是個空實現,會悄無聲息的吞噬你的的任務。所以這個策略基本上不用了。
3.DiscardOldestPolicy棄老策略:丟棄隊列最前面的任務,然后重新提交被拒絕的任務。
此拒絕策略,是一種喜新厭舊的拒絕策略。是否要采用此種拒絕策略,還得根據實際業務是否允許丟棄老任務來認真衡量。
功能:如果線程池未關閉,就彈出隊列頭部的元素,然后嘗試執行
使用場景:這個策略還是會丟棄任務,丟棄時也是毫無聲息,但是特點是丟棄的是老的未執行的任務,而且是待執行優先級較高的任務。基於這個特性,想到的場景就是,發布消息和修改消息,當消息發布出去后,還未執行,此時更新的消息又來了,這個時候未執行的消息的版本比現在提交的消息版本要低就可以被丟棄了。因為隊列中還有可能存在消息版本更低的消息會排隊執行,所以在真正處理消息的時候一定要做好消息的版本比較。
4.CallerRunsPolicy調用者運行策略:由調用線程處理該任務。
功能:當觸發拒絕策略時,只要線程池沒有關閉,就由提交任務的當前線程處理。
使用場景:一般在不允許失敗的、對性能要求不高、並發量較小的場景下使用,因為線程池一般情況下不會關閉,也就是提交的任務一定會被運行,但是由於是調用者線程自己執行的,當多次提交任務時,就會阻塞后續任務執行,性能和效率自然就慢了。
5.dubbo中的線程拒絕策略。
當dubbo的工作線程觸發了線程拒絕后,主要做了三個事情,原則就是盡量讓使用者清楚觸發線程拒絕策略的真實原因。
(1)輸出了一條警告級別的日志,日志內容為線程池的詳細設置參數,以及線程池當前的狀態,還有當前拒絕任務的一些詳細信息。可以說,這條日志,使用dubbo的有過生產運維經驗的或多或少是見過的,這個日志簡直就是日志打印的典范,其他的日志打印的典范還有spring。得益於這么詳細的日志,可以很容易定位到問題所在。
(2)輸出當前線程堆棧詳情,這個太有用了,當你通過上面的日志信息還不能定位問題時,案發現場的dump線程上下文信息就是你發現問題的救命稻草。
(3)繼續拋出拒絕執行異常,使本次任務失敗,這個繼承了JDK默認拒絕策略的特性。
6.Netty中的線程池拒絕策略。
Netty中的實現很像JDK中的CallerRunsPolicy,舍不得丟棄任務。不同的是,CallerRunsPolicy是直接在調用者線程執行的任務。而 Netty是新建了一個線程來處理的。所以,Netty的實現相較於調用者執行策略的使用面就可以擴展到支持高效率高性能的場景了。但是也要注意一點,Netty的實現里,在創建線程時未做任何的判斷約束,也就是說只要系統還有資源就會創建新的線程來處理,直到new不出新的線程了,才會拋創建線程失敗的異常。
7.activeMq中的線程池拒絕策略。
activeMq中的策略屬於最大努力執行任務型,當觸發拒絕策略時,在嘗試一分鍾的時間重新將任務塞進任務隊列,當一分鍾超時還沒成功時,就拋出異常。
8.pinpoint中的線程池拒絕策略。
pinpoint的拒絕策略實現很有特點,和其他的實現都不同。他定義了一個拒絕策略鏈,包裝了一個拒絕策略列表,當觸發拒絕策略時,會將策略鏈中的rejectedExecution依次執行一遍。