多線程捕獲線程中的異常

  1. Thread類最佳實踐: 
    • 寫的時候最好要設置線程名稱 Thread.name,並設置線程組 ThreadGroup,目的是方便管理。在出現問題的時候,打印線程棧 (jstack -pid) 一眼就能夠看出是哪一個線程出的問題,這個線程是幹什麼的。
  2. 二種實現方式

方法一 Thread方式經過線程組,線程名,並設置UncaughtExceptionHandler來捕獲異常

public static void main(String[] args) { try{ Thread t =new Thread(new Runnable(){ @Override public void run() { int i = 10/0; System.out.println("run...."); } }); t.setUncaughtExceptionHandler( new UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { System.out.println("catch 到了"); } }); t.start(); }catch(Exception e){ System.out.println("catch 不到"); } }

輸出以下: 
catch 到了java

public static void main(String[] args) { try{ Thread t =new Thread(new Runnable(){ @Override public void run() { int i = 10/0; System.out.println("run...."); } }); t.start(); }catch(Exception e){ System.out.println("catch 不到"); } }

輸出以下:ide

Exception in thread "Thread-0" java.lang.ArithmeticException: / by zero at com.credit.subject.traditional.ThreadLocalTest$1.run(ThreadLocalTest.java:12) at java.lang.Thread.run(Thread.java:722) 即便不加Handler 也catch不到,說明線程不能用try,catch來獲取線程中的異常。須要使用Handler。

方法二 使用ExecutorService來捕獲線程

- 因爲線程的本質特性,使得你不能捕獲從線程中逃逸的異常。一旦異常逃出任務的run()方法它就會向外傳播到控制檯,除非你採起特殊的步驟捕獲這種錯誤的異常。
   - 在Java SE5以前,你可使用線程組來捕捉這種異常,可是有了Java SE5,就能夠用Executor來解決這個問題了。
   - 下面的任務老是會拋出一個異常,該異常會傳播到其run()方法的外部,而且main()展現了當你運行它時所發生的事情:
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ExceptionThread implements Runnable { public void run() { throw new RuntimeException(); } public static void main(String[] args) { ExecutorService service = Executors.newCachedThreadPool(); service.execute(new ExceptionThread()); } }

輸出以下:spa

Exception in thread "pool-1-thread-1" java.lang.RuntimeException at com.abc.thread.ExceptionThread.run(ExceptionThread.java:6) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at java.lang.Thread.run(Thread.java:745)

將main的主體放在try-catch語句塊中也是沒有做用的:.net

import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ExceptionThread implements Runnable { public void run() { throw new RuntimeException(); } public static void main(String[] args) { try { ExecutorService service = Executors.newCachedThreadPool(); service.execute(new ExceptionThread()); } catch (RuntimeException e) { System.out.println("Catched Runtime Exception."); } } }

這將產生於前面示例相同的結果:未捕獲的異常。 
- 爲了解決這個問題,咱們要修改Executor產生線程的方式。Thread.UncaughtExceptionHandler是Java SE5中的新接口,它容許你在每一個Thread對象上都附着一個異常處理器。 
- Thread.UncaughtExceptionHandler.uncaughtException()會在線程因未捕獲的異常而臨近死亡時被調用。爲了使用它,咱們建立了一個新類型的ThreadFactory,它將在每一個新建立的Thread對象上附着一個Thread.UncaughtExceptionHandler。線程

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

public class ExceptionThread2 implements Runnable { public void run() { throw new RuntimeException("NullPointer"); } public static void main(String[] args) { ThreadFactory tFactory = new MyThreadFactory(); ExecutorService service = Executors.newCachedThreadPool(tFactory); Runnable task = new ExceptionThread2(); service.execute(task); } } class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler { // 處理從線程裏拋出來的異常。 public void uncaughtException(Thread t, Throwable e) { System.out.println("Catched Throwable: " + e.getClass().getSimpleName() + ", " + e.getMessage()); } } class MyThreadFactory implements ThreadFactory { // 從新組織建立線程的方式 public Thread newThread(Runnable r) { Thread t = new Thread(r); // 爲每個線程都綁定一個異常處理器。 t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler()); System.out.println("Thread[" + t.getName() + "] created."); return t; } }

執行的結果以下: 
這裏寫圖片描述 
能夠看到,線程池中有2個線程,當一個線程發生異常時,該異常被捕捉了。code

上面的示例使得你能夠按照具體狀況(在newThread()方法中使用if, case等語句)爲每一個線程逐個的設置處理器。若是你知道將要在代碼中到處使用相同的異常處理器,那麼更簡單的方式是在Thread類中設置一個靜態域,並將這個處理器設置爲默認的處理器便可:對象

import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class SettingDefaultHandler { public static void main(String[] args) { // 爲線程設置默認的異常處理器。 Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler()); ExecutorService exec = Executors.newCachedThreadPool(); exec.execute(new ExceptionThread2()); } }

這個處理器只有在不存在線程專有的未捕獲異常處理器的狀況下才會被調用。系統會檢查線程專有版本,若是沒有發現,則檢查線程組是否有專有的uncaughtException()方法,若是也沒有,纔會調用defaultUncaughtExceptionHandler.blog

轉載至:https://blog.csdn.net/pad12270828/article/details/54582646接口

相關文章
相關標籤/搜索