线程内部的run方法可以向外抛出异常吗?

提出问题

线程的run方法向外可以抛出异常吗,或者能被主线程捕获异常吗?比如下面这段代码:

public static void main(String[] args) {
    try {
        new Thread(()->{
            throw new Exception();
        }).start();
    }catch (Exception e){
        System.out.println("捕获异常!");
    }
}

能被外部主线程捕获到异常吗?如果你用的是比较智能的IDE,应该在编写这段代码时就会直接报错:未报告的异常错误java.lang.Exception; 必须对其进行捕获或声明以便抛出,可见外部是捕获不到这个异常的,同理,下面这段代码会产生一个RuntimeException,在主线程中也不能对其进行捕获:

public static void main(String[] args) {
    try {
        new Thread(()->{
            int i = 1/0;
        }).start();
    }catch (Exception e){
        System.out.println("捕获异常!");
    }
}

处理线程内部异常的方法

try-catch

由此可知,java线程内部产生的异常应当在线程内部进行处理,最典型的方法是在线程内部使用try...catch...捕获异常。

但是,如果不想使用try-catch,有没有其他的办法呢?

UncaughtExceptionHandler

JDK提供了一个类称为UncaughtExceptionHandler,翻译过来是未捕获异常处理器,可以把它看作线程的全局异常处理器,会处理所有的RuntimeException异常,因为它是非受查的。

给线程设置一个UncaughtExceptionHandler有两个方法,一个是直接使用Thread的成员方法setUncaughtExceptionHandler,是为调用该方法的线程设置异常处理器:

public static void main(String[] args) {
    Thread thread = new Thread(()->{
        int i = 1/0;
    },"thread1");

    thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
        @Override
        public void uncaughtException(Thread t, Throwable e) {
            System.out.println("在"+Thread.currentThread().getName()+"中进行处理异常:");
            System.out.println(e.getClass().getName());
        }
    });

    thread.start();
}

另一种方法是通过Thread.setDefaultUncaughtExceptionHandler静态方法,为所有线程设置一个异常处理器:

public static void main(String[] args) {
    Thread thread = new Thread(()->{
        int i = 1/0;
    },"thread1");

    Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
        @Override
        public void uncaughtException(Thread t, Throwable e) {
            System.out.println("在"+Thread.currentThread().getName()+"中进行处理异常:");
            System.out.println(e.getClass().getName());
        }
    });

    thread.start();
}

上面这两段程序运行之后都可以看到异常是在哪进行处理的:

在thread1中进行处理异常:
java.lang.ArithmeticException

可见还是在子线程中进行处理的,那么有没有能在主线程中处理异常的方法呢?

TaskFuture+Callable触发异常

public static void main(String[] args) throws InterruptedException, ExecutionException {
    FutureTask<Integer> futureTask = new FutureTask<>(()->{
        int i = 1/0;
        return i;
    });

    Thread thread = new Thread(futureTask);

    thread.start();
    System.out.println(futureTask.get());
}

运行这段程序,查看错误流输出:

Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero
    at java.util.concurrent.FutureTask.report(FutureTask.java:122)
    at java.util.concurrent.FutureTask.get(FutureTask.java:192)
    at com.rhett.thread.TestCatch.main(TestCatch.java:16)
Caused by: java.lang.ArithmeticException: / by zero
    at com.rhett.thread.TestCatch.lambdamain0(TestCatch.java:9)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.lang.Thread.run(Thread.java:748)

可以看到,触发了一个ExecutionException异常,并且是在主线程内触发的,它的stacktrace信息和caused by信息都说明了是因为子线程中的哪个异常而引起的ExecutionException,通过下面这个程序就可以在主线程中处理异常了:

public static void main(String[] args) throws InterruptedException, ExecutionException {
    FutureTask<Integer> futureTask = new FutureTask<>(()->{
        int i = 1/0;
        return i;
    });

    Thread thread = new Thread(futureTask);

    thread.start();
    try {
        System.out.println(futureTask.get());
    }catch (ExecutionException e){
        System.out.println(e.getCause());
    }
}

原创文章,作者:彭晨涛,如若转载,请注明出处:https://www.codetool.top/article/%e7%ba%bf%e7%a8%8b%e5%86%85%e9%83%a8%e7%9a%84run%e6%96%b9%e6%b3%95%e5%8f%af%e4%bb%a5%e5%90%91%e5%a4%96%e6%8a%9b%e5%87%ba%e5%bc%82%e5%b8%b8%e5%90%97%ef%bc%9f/