Java 線程異常處理---UncaughtExceptionHandler

介紹

在多線程中,主線程是無法捕獲到子線程的異常的。除非所有的異常能夠在run()中被捕獲,否則將會把異常棧信息輸出到終端而中斷該線程。所以Thread提供了UncaughtExceptionHandler來供我們在線程外部進行操作,確保子線程未捕獲的異常能夠被處理。


源碼分析

  • Thread類中提供的接口
	@FunctionalInterface
    public interface UncaughtExceptionHandler {
        void uncaughtException(Thread t, Throwable e);
    } 
  • Thread類中提供的方法
	private volatile UncaughtExceptionHandler uncaughtExceptionHandler;
	//用於對特定的線程設置異常處理器。
    public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
        checkAccess();
        uncaughtExceptionHandler = eh;
    }	
	//用於獲取特定的線程的異常處理器。
	public UncaughtExceptionHandler getUncaughtExceptionHandler() {
        //若當前的線程沒有異常處理器,會使用所屬線程組的異常處理器
        	//詳見下面的ThreadGroup.uncaughtException
        return uncaughtExceptionHandler != null ?
            uncaughtExceptionHandler : group;
    }
	

	private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler;
	//靜態方法:用於設置一個默認的全局異常處理器
    public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(
                new RuntimePermission("setDefaultUncaughtExceptionHandler")
                    );
        }
         defaultUncaughtExceptionHandler = eh;
     }
    //靜態方法:用於獲取一個默認的全局異常處理器
    public static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler(){
        return defaultUncaughtExceptionHandler;
    }
  • ThreadGroup.uncaughtException
	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);
            }
        }
    }

當發生異常時,它的調用順序是這樣:

  1. 查看是否有自己對象特有的 handler,如果有就直接處理
  2. 如果沒有,則調用所屬線程組的handler,這個過程會向上追溯父線程組的uncaughtException
  3. 若還是沒有被處理,則使用全局的handler
  4. 這時候若沒有全局的handler,則直接執行正常的異常流程中斷該線程

Demo

先定義一個target,讓其會拋出ArithmeticException

public class RunnableImpl implements Runnable{
    @Override
    public void run() {
        int i = 100/0;
    }
}

定義異常處理器

//全局的異常處理器
public class GlobalExceptionHander implements Thread.UncaughtExceptionHandler {
    @Override
    public void uncaughtException(Thread t, Throwable e) {
        System.out.println("線程"+t.getName()+"||全局的異常處理器||"+e.getMessage());
    }
}
//特定線程的異常處理器
public class ExceptionHandler implements Thread.UncaughtExceptionHandler{
    public void uncaughtException(Thread t, Throwable e) {
        System.out.println("線程"+t.getName()+"||特定異常處理器||"+e.getMessage());
    }
}

測試

public class ExceptionDemo {
    public static void main(String[] args) throws InterruptedException {
        //設置全局的異常處理器
        Thread.setDefaultUncaughtExceptionHandler(new GlobalExceptionHander());
	    //線程一指定了自己的異常處理器
        Thread thread1 = new Thread(new RunnableImpl(),"Thread1");
        thread1.setUncaughtExceptionHandler(new ExceptionHandler());
        thread1.start();
		//線程二不使用特定的異常處理器
        Thread thread2 = new Thread(new RunnableImpl(),"Thread2");
        thread2.start();
    }
}

結果
在這裏插入圖片描述

結果符合上述源碼分析的順序預期

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章