1. 程式人生 > >新建執行緒的異常處理

新建執行緒的異常處理

將Thinking injava 12.2.14節進行總結

一般情況:

由於執行緒的本質特性,使你不能捕獲從執行緒中逃逸的異常。一旦異常逃出任務的run方法,它就會向外傳播到控制檯

package hfi.bellychang.Task.CaptureException.Demo01;

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

/**
 * 子執行緒中的異常在主執行緒中是捕捉不到的
 * Created by Administrator on 2017/6/26.
 */

class ExceptionThread implements Runnable {
    Logger logger = LoggerFactory.getLogger(ExceptionThread.class);
    ExceptionThread(){
        super();
        logger.info("exception happens in ExceptionThread");
    }
    @Override
    public void run() {
        throw new RuntimeException();
    }
}

public class MainThread {
    @Test
    public void test() {
        ExecutorService exec = Executors.newCachedThreadPool();
        exec.execute(new ExceptionThread());
    }

}

執行結果:異常向外傳播到控制檯



問題自然來了,如何捕捉子執行緒中的異常,使其不直接向外傳播到控制檯呢?

解決方案1:

實現ThreadFactory介面,在newThread方法中附加異常處理器。將這個工廠傳遞給Executors建立新的ExecutorService的方法,就設定了這個執行緒池專有的未捕獲異常的處理器

package hfi.bellychang.Task.CaptureException.Demo02;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

/**
 * 在主執行緒中捕獲子執行緒產生的異常
 * 要修改Executor產生錢程的方式
 * Thread.UncaughtExceptionHandler是Java SE5 中的新介面,
 * 它允許你在每個Thread物件上都附著一個異常處理器o
 * Thread.UncaughtExceptionHandler。uncaughtException()會線上程因未捕獲的異常而臨近死亡時被呼叫
 *
 * Created by Administrator on 2017/6/26.
 */

class ExceptionThread2 implements Runnable {

    public void run() {
        throw new RuntimeException();
    }
}

class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {

    Logger logger = LoggerFactory.getLogger(MyUncaughtExceptionHandler.class);

    @Override
    public void uncaughtException(Thread t, Throwable e) {
        logger.info("caught " + e);
    }
}

/**
 * 建立了一個新型別的ThreadFactory, 它將在每個新建立的Thread物件上
 * 附著一個Thread.UncaughtExceptionHandler
 */
class HandlerThreadFactory implements ThreadFactory {

    Logger logger = LoggerFactory.getLogger(this.getClass());

    @Override
    public Thread newThread(Runnable r) {
        Thread t = new Thread(r);
        logger.info("created " + t);
        //附著一個Thread.UncaughtExceptionHandler
        t.setUncaughtExceptionHandler(
                new MyUncaughtExceptionHandler());
        return t;
    }
}

/**
 * 示例使得你可以按照具體情況逐個地設定處理器
 * 意思是通過不同的ThreadFactory的實現 附著不同的ExceptionHandler?
 */
public class MainThread {
    public static void main(String[] args) {
        //我們將這個工廠傳遞給Executors建立新的ExecutorService的方法:

        //此執行緒池專有的未捕獲異常處理器
        ExecutorService exec = Executors.newCachedThreadPool(
                new HandlerThreadFactory());
        exec.execute(new ExceptionThread2());
    }
}

執行結果

可見通過不同的ThreadFactory的實現,附著不同的ExceptionHandler,可以按照具體情況逐個地設定處理器

解決方案2:

在Thread類中設定一個靜態域,並將這個處理器設定為Thread類的預設的未捕獲異常處理器 Thread.setDefaultUncaughtExceptionHandler,但這樣不能指定某個Thread使用哪個異常處理器

package hfi.bellychang.Task.CaptureException.Demo03;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

/**
 * 通過這種方式,在主執行緒中捕獲到了子執行緒中的異常
 * Created by Administrator on 2017/6/26.
 */

class ExceptionThread implements Runnable {
    public void run() {
        throw new RuntimeException();
    }
}

class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {

    Logger logger = LoggerFactory.getLogger(this.getClass());

    @Override
    public void uncaughtException(Thread t, Throwable e) {
        logger.info("caught " + e);
    }
}

public class MainThread {
    public static void main(String[] args) {
        Thread.setDefaultUncaughtExceptionHandler(
                new MyUncaughtExceptionHandler());
        ExecutorService exec = Executors.newCachedThreadPool();
        exec.execute(new ExceptionThread());
    }
}

通過以上的兩種方式,就可以將異常資訊在catch中寫入日誌檔案,在日誌檔案中檢視子執行緒是否發生異常,對程式的執行情況有所掌握