使用线程池捕获线程异常

        在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 = com.example.redisqueue.utils.MyUncaughtExceptionHandler@9e89d68
准备抛出异常
创建一个新的线程
eh121 = com.example.redisqueue.utils.MyUncaughtExceptionHandler@7f1b8298
线程名字====专属线程名字-1
捕获异常java.lang.RuntimeException

猜你喜欢

转载自blog.csdn.net/zsj777/article/details/84999121