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

使用執行緒池捕獲執行緒異常

        在java多執行緒程式中,所有執行緒都不允許丟擲未捕獲的checked exception(比如sleep時的InterruptedException),也就是說各個執行緒需要自己把自己的checked exception處理掉。這一點是通過java.lang.Runnable.run()方法宣告(因為此方法宣告上沒有throw exception部分)進行了約束。但是執行緒依然有可能丟擲unchecked exception(如執行時異常),當此類異常跑丟擲時,執行緒就會終結,而對於主執行緒和其他執行緒完全不受影響,且完全感知不到某個執行緒丟擲的異常(也是說完全無法catch到這個異常)。JVM的這種設計源自於這樣一種理念:“執行緒是獨立執行的程式碼片斷,執行緒的問題應該由執行緒自己來解決,而不要委託到外部。”基於這樣的設計理念,在Java中,執行緒方法的異常(無論是checked還是unchecked exception),都應該線上程程式碼邊界之內(run方法內)進行try catch並處理掉.換句話說,我們不能捕獲從執行緒中逃逸的異常。

看下面的例子:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExceptionThread implements Runnable {

    @Override
    public void run() {
        System.out.println(111);
        throw new RuntimeException("這個執行緒就幹了這麼一件事,丟擲一個執行時異常");
    }

    public static void main(String[] args) {
        try {
            ExecutorService exec = Executors.newCachedThreadPool();
            exec.execute(new ExceptionThread());
            System.out.println("該幹嘛幹嘛去");
        } catch (RuntimeException e) {
            System.out.println("能不能捕獲到異常?");
        }

    }

}

執行結果如下:

該幹嘛幹嘛去
111
Exception in thread "pool-1-thread-1" java.lang.RuntimeException: 這個執行緒就幹了這麼一件事,丟擲一個執行時異常
	at com.example.redisqueue.web.ExceptionThread.run(ExceptionThread.java:15)
	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)

 

從執行結果中,我們可以看到的是,這個異常在main執行緒中沒有catch到,即

    System.out.println("能不能捕獲到異常?");

永遠不會執行到。

        問題來了,我們如果需要捕獲其執行緒的unchecked異常時該怎麼辦?Java SE5之後,我們可以通過Executor來解決這個我問題。為了解決這個問題,我們需要修改Executor產生執行緒的方式。Thread.UncaughtExceptionHandler是java SE5中的新介面,它允許我們在每一個Thread物件上新增一個異常處理器。(UncaughtExceptionHandler)。Thread.UncaughtExceptionHandler.uncaughtException()方法會線上程因未捕獲的異常而面臨死亡時被呼叫。

注意:在使用執行緒池的時候

在我們執行緒池中會重設定新的Thread物件,而這個Thread物件沒有設定任何異常處理器,換句話說,我們線上程池外對執行緒做的 任何操作都是沒有用的。

在使用執行緒池怎麼設定異常處理器?

解決方案如下:

1、建立執行緒異常捕獲類 

MyUncaughtExceptionHandler
public class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {

    @Override
    public void uncaughtException(Thread t, Throwable e) {
        System.out.println("執行緒名字===="+t.getName());
        System.out.println("捕獲異常" + e.toString());
    }

}

2、建立

HandlerThreadFactory

該類用於在使用執行緒池建立執行緒時設定執行緒的異常捕獲類和執行緒的名字。

public class HandlerThreadFactory implements ThreadFactory {
    private final AtomicInteger threadNumber = new AtomicInteger(1);
    //執行緒名字字首
    private final String namePrefix;

    public HandlerThreadFactory(String namePrefix) {
        this.namePrefix = namePrefix+"-";
    }

    @Override
    public Thread newThread(Runnable r) {
        Thread t = new Thread( r,namePrefix + threadNumber.getAndIncrement());
        System.out.println("建立一個新的執行緒");
        t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
        System.out.println("eh121 = " + t.getUncaughtExceptionHandler());
        return t;
    }

}

 

3、執行類

ExceptionThread
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());
        //建立執行緒池 指定執行緒池建立執行緒的 ThreadFactory 並設定執行緒名字
        ExecutorService service = Executors.newCachedThreadPool(new HandlerThreadFactory("專屬執行緒名字"));
        service.execute(thread);
    }

}

執行結果如下:

建立一個新的執行緒
eh121 = [email protected]
準備丟擲異常
建立一個新的執行緒
eh121 = [email protected]
執行緒名字====專屬執行緒名字-1
捕獲異常java.lang.RuntimeException