JAVA多线程之UncaughtExceptionHandler——处理非正常的线程中止
歡迎跳轉到本文的原文鏈接:https://honeypps.com/java/java-multi-thread-of-uncaught-exception-handler/
當單線程的程序發生一個未捕獲的異常時我們可以采用try....catch進行異常的捕獲,但是在多線程環境中,線程拋出的異常是不能用try....catch捕獲的,這樣就有可能導致一些問題的出現,比如異常的時候無法回收一些系統資源,或者沒有關閉當前的連接等等。
首先來看一個示例:
?
package com.exception;public class NoCaughtThread {public static void main(String[] args){try{Thread thread = new Thread(new Task());thread.start();}catch (Exception e){System.out.println("==Exception: "+e.getMessage());}} }class Task implements Runnable {@Overridepublic void run(){System.out.println(3/2);System.out.println(3/0);System.out.println(3/1);} }運行結果:
?
?
1 Exception in thread "Thread-0" java.lang.ArithmeticException: / by zeroat com.exception.Task.run(NoCaughtThread.java:25)at java.lang.Thread.run(Unknown Source)可以看到在多線程中通過try....catch試圖捕獲線程的異常是不可取的。
?
Thread的run方法是不拋出任何檢查型異常的,但是它自身卻可能因為一個異常而被中止,導致這個線程的終結。
首先介紹一下如何在線程池內部構建一個工作者線程,如果任務拋出了一個未檢查異常,那么它將使線程終結,但會首先通知框架該現場已經終結。然后框架可能會用新的線程來代替這個工作線程,也可能不會,因為線程池正在關閉,或者當前已有足夠多的線程能滿足需要。當編寫一個向線程池提交任務的工作者類線程類時,或者調用不可信的外部代碼時(例如動態加載的插件),使用這些方法中的某一種可以避免某個編寫得糟糕的任務或插件不會影響調用它的整個線程。
運行結果:
?
?
1 ==Exception: / by zero上面介紹了一種主動方法來解決未檢測異常。在Thread ApI中同樣提供了UncaughtExceptionHandle,它能檢測出某個由于未捕獲的異常而終結的情況。這兩種方法是互補的,通過將二者結合在一起,就能有效地防止線程泄露問題。
?
如下:
?
package com.exception;import java.lang.Thread.UncaughtExceptionHandler;public class WitchCaughtThread {public static void main(String args[]){Thread thread = new Thread(new Task());thread.setUncaughtExceptionHandler(new ExceptionHandler());thread.start();} }class ExceptionHandler implements UncaughtExceptionHandler {@Overridepublic void uncaughtException(Thread t, Throwable e){System.out.println("==Exception: "+e.getMessage());} }運行結果:
?
?
1 ==Exception: / by zero同樣可以為所有的Thread設置一個默認的UncaughtExceptionHandler,通過調用Thread.setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)方法,這是Thread的一個static方法。
?
如下:
?
package com.exception;import java.lang.Thread.UncaughtExceptionHandler;public class WitchCaughtThread {public static void main(String args[]){Thread.setDefaultUncaughtExceptionHandler(new ExceptionHandler());Thread thread = new Thread(new Task());thread.start();} }class ExceptionHandler implements UncaughtExceptionHandler {@Overridepublic void uncaughtException(Thread t, Throwable e){System.out.println("==Exception: "+e.getMessage());} }?
運行結果:
1 ==Exception: / by zero?
如果采用線程池通過execute的方法去捕獲異常,先看下面的例子:
?
public class ExecuteCaught {public static void main(String[] args){ExecutorService exec = Executors.newCachedThreadPool();Thread thread = new Thread(new Task());thread.setUncaughtExceptionHandler(new ExceptionHandler());exec.execute(thread);exec.shutdown();} }ExceptionHandler可參考上面的例子,運行結果:
?
?
1 Exception in thread "pool-1-thread-1" java.lang.ArithmeticException: / by zeroat com.exception.Task.run(NoCaughtThread.java:25)at java.lang.Thread.run(Unknown Source)at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)at java.lang.Thread.run(Unknown Source)可以看到并未捕獲到異常。
?
這時需要將異常的捕獲封裝到Runnable或者Callable中,如下所示:
package com.exception;import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;public class ExecuteCaught {public static void main(String[] args){ExecutorService exec = Executors.newCachedThreadPool();exec.execute(new ThreadPoolTask());exec.shutdown();} }class ThreadPoolTask implements Runnable {@Overridepublic void run(){Thread.currentThread().setUncaughtExceptionHandler(new ExceptionHandler());System.out.println(3/2);System.out.println(3/0);System.out.println(3/1);} }?
運行結果:
1 ==Exception: / by zero只有通過execute提交的任務,才能將它拋出的異常交給UncaughtExceptionHandler,而通過submit提交的任務,無論是拋出的未檢測異常還是已檢查異常,都將被認為是任務返回狀態的一部分。如果一個由submit提交的任務由于拋出了異常而結束,那么這個異常將被Future.get封裝在ExecutionException中重新拋出。
下面兩個例子:
?
package com.exception;import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;public class SubmitCaught {public static void main(String[] args){ExecutorService exec = Executors.newCachedThreadPool();exec.submit(new Task());exec.shutdown();} } package com.exception;import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;public class SubmitCaught {public static void main(String[] args){ExecutorService exec = Executors.newCachedThreadPool();exec.submit(new ThreadPoolTask());exec.shutdown();} }運行結果都是:
?
?
1這樣可以證實我的觀點。接下來通過這個例子可以看到捕獲的異常:
package com.exception;import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future;public class SubmitCaught {public static void main(String[] args){ExecutorService exec = Executors.newCachedThreadPool();Future<?> future = exec.submit(new Task());exec.shutdown();try{future.get();}catch (InterruptedException | ExecutionException e){System.out.println("==Exception: "+e.getMessage());}} }運行結果:
?
?
1 ==Exception: java.lang.ArithmeticException: / by zero希望我整理的這些能夠給各位有所幫助。
?
歡迎跳轉到本文的原文鏈接:https://honeypps.com/java/java-multi-thread-of-uncaught-exception-handler/
歡迎支持筆者新作:《深入理解Kafka:核心設計與實踐原理》和《RabbitMQ實戰指南》,同時歡迎關注筆者的微信公眾號:朱小廝的博客。總結
以上是生活随笔為你收集整理的JAVA多线程之UncaughtExceptionHandler——处理非正常的线程中止的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JAVA多线程之扩展ThreadPool
- 下一篇: JAVA线程间协作:wait.notif