1. 程式人生 > >子執行緒中的異常處理

子執行緒中的異常處理

前2種方法都是在子執行緒中處理,第3種方法是在父執行緒中處理。
具體用哪一種方法,取決於這個異常是否適合在子執行緒中處理。例如有些異常更適合由呼叫方(父執行緒)處理,那麼此時就應當用第3種方法。


方法一:子執行緒中try... catch...
最簡單有效的辦法,就是在子執行緒的執行方法中,把可能發生異常的地方,用try ... catch ... 語句包起來。
子執行緒程式碼:

public class ChildThread implements Runnable {
    public void run() {
        doSomething1();
        try {
            // 可能發生異常的方法
            exceptionMethod();
        } catch (Exception e) {
            // 處理異常
            System.out.println(String.format("handle exception in child thread. %s", e));
        }
        doSomething2();
    }
}

 

方法二:為執行緒設定“未捕獲異常處理器”UncaughtExceptionHandler
為執行緒設定異常處理器。具體做法可以是以下幾種:
(1)Thread.setUncaughtExceptionHandler設定當前執行緒的異常處理器;
(2)Thread.setDefaultUncaughtExceptionHandler為整個程式設定預設的異常處理器;
如果當前執行緒有異常處理器(預設沒有),則優先使用該UncaughtExceptionHandler類;否則,如果當前執行緒所屬的執行緒組有異常處理器,則使用執行緒組的
UncaughtExceptionHandler;否則,使用全域性預設的DefaultUncaughtExceptionHandler;如果都沒有的話,子執行緒就會退出。
注意:子執行緒中發生了異常,如果沒有任何類來接手處理的話,是會直接退出的,而不會記錄任何日誌。
所以,如果什麼都不做的話,是會出現子執行緒任務既沒執行成功,也沒有任何日誌提示的“詭異”現象的。
設定當前執行緒的異常處理器

public class ChildThread implements Runnable {    
    private static ChildThreadExceptionHandler exceptionHandler;

    static {
        exceptionHandler = new ChildThreadExceptionHandler();
    }

    public void run() {
        Thread.currentThread().setUncaughtExceptionHandler(exceptionHandler);
        System.out.println("do something 1");
        exceptionMethod();
        System.out.println("do something 2");
    }

    public static class ChildThreadExceptionHandler implements Thread.UncaughtExceptionHandler {
        public void uncaughtException(Thread t, Throwable e) {
            System.out.println(String.format("handle exception in child thread. %s", e));
        }
    }
}

或者,設定所有執行緒的預設異常處理器

public class ChildThread implements Runnable {
    private static ChildThreadExceptionHandler exceptionHandler;

    static {
        exceptionHandler = new ChildThreadExceptionHandler();
        Thread.setDefaultUncaughtExceptionHandler(exceptionHandler);
    }

    public void run() {
        System.out.println("do something 1");
        exceptionMethod();
        System.out.println("do something 2");
    }

    private void exceptionMethod() {
        throw new RuntimeException("ChildThread exception");
    }

    public static class ChildThreadExceptionHandler implements Thread.UncaughtExceptionHandler {
        public void uncaughtException(Thread t, Throwable e) {
            System.out.println(String.format("handle exception in child thread. %s", e));
        }
    }
}

方法三:通過Future的get方法捕獲異常(推薦)
使用執行緒池提交一個能獲取到返回資訊的方法,也就是ExecutorService.submit(Callable)
在submit之後可以獲得一個執行緒執行結果的Future物件,而如果子執行緒中發生了異常,通過future.get()獲取返回值時,可以捕獲到
ExecutionException異常,從而知道子執行緒中發生了異常。
子執行緒程式碼:

public class ChildThread implements Callable<String> {
    public String call() throws Exception {
        System.out.println("do something 1");
        exceptionMethod();
        System.out.println("do something 2");
        return "test result";
    }

    private void exceptionMethod() {
        throw new RuntimeException("ChildThread1 exception");
    }
}

父執行緒程式碼:

public class Main {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(8);
        Future future = executorService.submit(new ChildThread());
        try {
            future.get();
        } catch (InterruptedException e) {
            System.out.println(String.format("handle exception in child thread. %s", e));
        } catch (ExecutionException e) {
            System.out.println(String.format("handle exception in child thread. %s", e));
        } finally {
            if (executorService != null) {
                executorService.shutdown();
            }
        }
    }
}