執行緒異常捕獲
由於Java執行緒的本質特徵,當丟擲異常的時候就終止瞭如果再進行catch,都不在一個執行緒裡面,所以無法捕捉到異常。
Java執行緒中,要在run()方法中把一切的異常都處理掉,可以使用try-catch塊。不能讓這個執行緒丟擲異常,因為如果我們不使用特殊的方式的話,我們是無法捕獲從這個執行緒中逃逸的異常的。異常一旦丟擲了,那麼這個執行緒就會停止執行,但是不會影響主執行緒和其它的執行緒。因為主執行緒和其它的執行緒都不知道它丟擲了異常。
先給出一個例子,執行緒在run()方法中丟擲異常,看main函式能不能catch到。
ExceptionThread.java
package thread.uncaughtException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Created by louyuting on 2017/3/11.
*/
public class ExceptionThread implements Runnable{
@Override
public void run() {
System.out.println("準備丟擲異常");
throw new RuntimeException();
}
public static void main(String[] args) {
Thread thread = new Thread(new ExceptionThread());
ExecutorService service = Executors.newCachedThreadPool();
service.execute(thread);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
看看執行結果:
Exception in thread "pool-1-thread-1" java.lang.RuntimeException
at thread.uncaughtException.ExceptionThread.run(ExceptionThread.java:14)
at java.lang.Thread.run(Thread.java:745)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
準備丟擲異常
- 1
- 2
- 3
- 4
- 5
- 6
- 7
明顯就是沒有catch到異常。 難道是因為我們沒有再main函式裡面用try catch塊包圍起來?接下來再做測試:
package thread.uncaughtException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Created by louyuting on 2017/3/11.
*/
public class ExceptionThread implements Runnable{
@Override
public void run() {
System.out.println("準備丟擲異常");
throw new RuntimeException();
}
public static void main(String[] args) {
Thread thread = new Thread(new ExceptionThread());
try {
ExecutorService service = Executors.newCachedThreadPool();
service.execute(thread);
} catch (Exception e) {
System.out.println("我捕捉到異常了");
e.printStackTrace();
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
執行結果:
Exception in thread "pool-1-thread-1" java.lang.RuntimeException
at thread.uncaughtException.ExceptionThread.run(ExceptionThread.java:14)
at java.lang.Thread.run(Thread.java:745)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
準備丟擲異常
- 1
- 2
- 3
- 4
- 5
- 6
- 7
執行結果還是一樣,說明並不是因為這個原因。
想想一個執行緒的丟擲異常之後就終止瞭如果再進行catch,都不在一個執行緒裡面。
為了解決這個問題,這個時候我們得去實現UncaughtExceptionHandler這個介面來捕獲丟擲的異常。UncaughtExceptionHandler是JDK5中的新介面,允許我們在每個執行緒上面都附加一個異常處理器,Thread.UncaughtExceptionHandler.uncaughtException()方法會線上程因未捕捉的異常而在臨近死亡時被呼叫。為了使用這個異常處理器我們建立一個新的執行緒工廠ThreadFactory。並在new執行緒時設定UncaughtExceptionHandler。
HandlerThreadFactory.java
package thread.uncaughtException;
import java.util.concurrent.ThreadFactory;
/**
* Created by louyuting on 2017/3/11.
*/
public class HandlerThreadFactory implements ThreadFactory {
@Override
public Thread newThread(Runnable r) {
System.out.println(this + "creating new thread");
Thread t = new Thread(r);
System.out.println("created" + t);
//為每一個執行緒設定異常捕獲器
t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
System.out.println("eh = " + t.getUncaughtExceptionHandler());
return t;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
這裡建立兩個丟擲異常的執行緒類
public class ExceptionThread2 implements Runnable {
@Override
public void run() {
Thread t = Thread.currentThread();
System.out.println("run by " + t.getName());
System.out.println("Exception " + t.getUncaughtExceptionHandler());
throw new RuntimeException();
}
}
public class ExceptionThread3 implements Runnable {
@Override
public void run() {
Thread t = Thread.currentThread();
System.out.println("run by " + t.getName());
System.out.println("Exception " + t.getUncaughtExceptionHandler());
throw new NullPointerException();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
實現我自己的異常處理器MyUncaughtExceptionHandler.java
public class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler{
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("\n [caugth:] " + e.toString());
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
測試主函式:
public class CaptureUncaughtException {
public static void main(String[] args) {
ExecutorService es = Executors.newCachedThreadPool(new HandlerThreadFactory());
es.execute(new ExceptionThread2());
es.execute(new ExceptionThread3());
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
執行結果是:
可知此時捕捉到了異常。
如果你知道將要在程式碼中處處使用相同的異常處理器,那麼更加簡單的方式是在Thread類中設定一個靜態域,並將這個處理器設定為預設的未捕獲異常的處理器。
public class SettingDefaultHandler {
public static void main(String[] args) {
//設定靜態異常處理器
Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
ExecutorService es = Executors.newCachedThreadPool();
es.execute(new ExceptionThread2());
}
}