1. 程式人生 > >執行緒異常捕獲

執行緒異常捕獲

由於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());
    }
}