关于java多线程中异常捕获的理解

在java多线程程序中,所有线程都不允许抛出未捕获的checked exception(比如sleep时的InterruptedException)也就是说各个线程需要自己把自己的checked exception处理掉

这句话怎么理解,最简单的看下图,也就是不能在Runnable的run方法上抛出异常,必须在里面捕获

这一点是通过java.lang.Runnable.run()方法声明(因为此方法声明上没有throw exception部分)进行了约束。但是线程依然有可能抛出unchecked exception(如运行时异常),当此类异常跑抛出时,线程就会终结,而对于主线程和其他线程完全不受影响,且完全感知不到某个线程抛出的异常(也是说完全无法catch到这个异常)。JVM的这种设计源自于这样一种理念:“线程是独立执行的代码片断,线程的问题应该由线程自己来解决,而不要委托到外部。”基于这样的设计理念,在Java中,线程方法的异常(无论是checked还是unchecked exception),都应该在线程代码边界之内(run方法内)进行try catch并处理掉.换句话说,我们不能捕获从线程中逃逸的异常。

看下面的例子:

public class ExceptionTest {

    public static void main(String[] args) {
        
        try {
            new Thread(new ExceptionTask()).start();
        } catch (Exception e) {
            System.out.println("注意看我是否能打印。。。");
            e.printStackTrace();
        }
    }
    
}

class ExceptionTask implements Runnable {

    @Override
    public void run() {
        System.out.println("".substring(0, 10));
    }
    
}

打印结果

Exception in thread "Thread-0" java.lang.StringIndexOutOfBoundsException: String index out of range: 10
    at java.lang.String.substring(Unknown Source)
    at com.actuator.ExceptionTask.run(ExceptionTest.java:21)
    at java.lang.Thread.run(Unknown Source)

从运行结果中,我们可以看到的是,"Thread-0"线程里的异常在main线程中没有catch到

问题来了,我们如果需要捕获"Thread-0"线程的unchecked异常时该怎么办?Java SE5之后,我们可以通过Executor来解决这个问题。

Thread.UncaughtExceptionHandler是java SE5中的新接口,它允许我们在每一个Thread对象上添加一个异常处理器。(UncaughtExceptionHandler)。Thread.UncaughtExceptionHandler.uncaughtException()方法会在线程因未捕获的异常而面临死亡时被调用

下面这个例子简单的演示了如何使用UncaughtExceptionHandler。

import java.lang.Thread.UncaughtExceptionHandler;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

public class ExceptionTest {

    public static void main(String[] args) {
        
        // 下面有两种方式来执行线程
        
        // 第一种,非线程池写法
        Thread t = new Thread(new ExceptionTask());
        t.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                System.out.println("线程"+t.getName()+"捕获到异常:"+e.getMessage());
            }
        });
        t.start();
        
        // 第二种,线程池写法
        ExecutorService executorService = Executors.newCachedThreadPool(new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                Thread t = new Thread(r);
                t.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
                    @Override
                    public void uncaughtException(Thread t, Throwable e) {
                        System.out.println("线程"+t.getName()+"捕获到异常:"+e.getMessage());
                    }
                });
                return t;
            }
        });
        executorService.execute(new ExceptionTask());
        
        
        
    }
    
}

class ExceptionTask implements Runnable {

    @Override
    public void run() {
        System.out.println("".substring(0, 10));
    }
    
}

两个执行结果一样

线程Thread-0捕获到异常:String index out of range: 10

 

posted @ 2019-01-28 22:49  夏威夷8080  阅读(1225)  评论(0编辑  收藏  举报