Java多執行緒探究-執行緒異常逃逸
執行緒逃逸
在Thread的run方法中,Java是不允許丟擲受檢異常的,所以必須由自己捕獲,但是對於一些執行時的異常,難免有時候完全捕獲到,繼而傳遞到上一層,導致不可預料的程式終止,所以需要在上一層捕獲
來看看我們到底不能捕獲未受檢異常
異常類
class MyRunnable implements Runnable {
private int ticket = 10000;
private Object object;
@Override
public void run() {
System.out.println(3/0);
System.out.println(3 /2);
System.out.println(4/0);
}
}
main方法
public class ThreadDemo {
public static void main(String[] args) {
try{
MyRunnable runnable = new MyRunnable();
Thread t1 = new Thread(runnable, "one");
Thread t2 = new Thread(runnable, "two");
Thread t3 = new Thread(runnable, "third");
Thread t4 = new Thread(runnable, "four");
t1.start();
t2.start();
t3.start();
t4.start();
}catch (Exception e){
System.out.println("捕獲了");
}
}
}
列印結果
可以看到,在main方法中,並沒有捕獲異常,導致異常的逃逸,造成意外的程式中斷
UncaughtExceptionHandler
是為了捕獲沒有被捕獲的異常,包括執行時異常,執行錯誤(記憶體溢位等),子執行緒丟擲的異常等
每個執行緒都可以單獨設定一個UncaughtExceptionHandler,線上程throw 了執行時異常的時候,異常被傳遞給未捕獲異常處理器UncaughtExceptionHandler是Thread的一個內部介面,我們自定一個Handler
public class ThreadDemo {
public static void main(String[] args) {
MyRunnable runnable = new MyRunnable();
Thread t1 = new Thread(runnable, "one");
Thread t2 = new Thread(runnable, "two");
Thread t3 = new Thread(runnable, "third");
Thread t4 = new Thread(runnable, "four");
t1.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("捕獲到了");
}
});
t1.start();
t2.start();
t3.start();
t4.start();
}
}
上面的程式碼中,t1這個執行緒的異常就可以會捕獲到並處理
也可以設定Thread的預設UncaughtException,使用Thread的靜態方法setDefaultUncaughtExceptionHandler
,這樣所有的執行緒的丟擲的異常都會被處理
MyRunnable runnable = new MyRunnable();
Thread t1 = new Thread(runnable, "one");
Thread t2 = new Thread(runnable, "two");
Thread t3 = new Thread(runnable, "third");
Thread t4 = new Thread(runnable, "four");
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("捕獲到了執行緒 "+t.getName()+" 異常資訊: "+e.getMessage());
}
});
t1.start();
t2.start();
t3.start();
t4.start();
輸出結果,所有執行緒的異常都被捕獲了
如果既沒有設定單獨的異常處理器,也沒有設定預設的異常處理器,那麼UncaughtException是ThreadGroup的處理器,ThreadGroup類實現了Thread.UncaughtExceptioin介面
ThreadGroup的呼叫規則
1.如果thread group有父group,那麼執行父group的uncaughtException
2.否則如果執行緒Thread設定了預設的UncaughtExceptoin,那麼呼叫預設的UncaughtExceptoin
3.否則如果異常不是ThreadDeath的例項(ThreadDeath物件是stop方法產生的),就打印出異常棧的資訊到System.err流
請看原始碼
void uncaughtException(Thread t, Throwable e)
calls this method of the parent thread group if there is a parent, or calls the default
handler of the Thread class if there is a default handler, or otherwise prints a stack
trace to the standard error stream. (However, if e is a ThreadDeath object, the stack trace
is suppressed. ThreadDeath objects are generated by the deprecated stopmethod.)
public void uncaughtException(Thread t, Throwable e) {
if (parent != null) {
parent.uncaughtException(t, e);
} else {
Thread.UncaughtExceptionHandler ueh =
Thread.getDefaultUncaughtExceptionHandler();
if (ueh != null) {
ueh.uncaughtException(t, e);
} else if (!(e instanceof ThreadDeath)) {
System.err.print("Exception in thread \""
+ t.getName() + "\" ");
e.printStackTrace(System.err);
}
}
}
預設建立的執行緒都會屬於同一個ThreadGroup,執行緒一個被執行緒組管理,也可以自定定義其他的執行緒組
關於ThreadGroup的使用,見我另一篇部落格