1. 程式人生 > >Java多執行緒探究-執行緒異常逃逸

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的使用,見我另一篇部落格