主线程如何获取子线程异常

发布时间 2023-11-29 19:45:09作者: kte66

主线程如何获取子线程异常

  • 常规情况:
    一般我们没有办法通过主线程去获取子线程的异常
    举个例子:
    public class test11_29{
        public static void main(String[] args) {
            try {
                Thread thread = new Thread(new myExceptionRunner());
                thread.start();
            } catch (Exception e) {
                //无法捕获子线程的异常
                System.out.println(""there is error! "");
                e.printStackTrace();
            }
            System.out.println("this is main");
        }
    }
    class myExceptionRunner implements Runnable{
    
        @Override
        public void run() {
            System.out.println("other thread is running");
            System.out.println("------------------");
            throw new RuntimeException("this is error!");
        }
    }
    
    结果如下:
    this is main
    running
    ------------------
    Exception in thread "Thread-0" java.lang.RuntimeException: this is error!
        at review.myExceptionRunner.run(test11_29.java:23)
        at java.base/java.lang.Thread.run(Thread.java:842)
    
    通过结果可以发现,子线程抛出错误,我们却无法进入到catch中的代码块,因此可以证明主线程是无法区获取子线程相关错误的
    因此,我们只能自己去实现获取子线程的错误
    对此,我们有两种方法:

第一种:通过自定义工厂方法实现

设置每一个线程执行时候的异常处理
1.自定义工厂方法

   class HandlerThreadFactory implements ThreadFactory{

       @Override
       public Thread newThread(Runnable r) {
           System.out.println("create thread t");
           Thread thread = new Thread(r);
           System.out.println("set uncaughtException for t");
           //将自定义好的拦截方法设置进去
           thread.setUncaughtExceptionHandler(new MyUncaughExceptionHandler());
           return thread;
       }
   }

2.自定义错误拦截方法

    class MyUncaughExceptionHandler implements Thread.UncaughtExceptionHandler{

        @Override
        public void uncaughtException(Thread t, Throwable e) {
            System.out.println("caught:" + e);
        }
    }

3.捕捉自线程错误实现

    public class test11_29 {
        public static void main(String[] args) {
            System.out.println("this is main");
            //必须我们自己实现
            System.out.println("第一种");
            //通过自定义工厂方法实现
            //自定义工厂方法作为创建线程池的参数
            ExecutorService newCachedThreadPool = Executors.newCachedThreadPool(new HandlerThreadFactory());
            //执行相关线程方法
            newCachedThreadPool.execute(new myExceptionRunner());
            newCachedThreadPool.shutdown();
        }
    }

4.汇总在一起,代码如下

    public class test11_29 {
        public static void main(String[] args) {
            System.out.println("this is main");
            //必须我们自己实现
            System.out.println("第一种");
            //通过自定义工厂方法实现
             //自定义工厂方法作为创建线程池的参数
            ExecutorService newCachedThreadPool = Executors.newCachedThreadPool(new HandlerThreadFactory());
            //执行相关线程方法
            newCachedThreadPool.execute(new myExceptionRunner());
            newCachedThreadPool.shutdown();
        }
    }
    class myExceptionRunner implements Runnable{
        @Override
        public void run() {
            System.out.println("other thread is running");
            System.out.println("------------------");
            throw new RuntimeException("this is error!");
        }
    }
    class MyUncaughExceptionHandler implements Thread.UncaughtExceptionHandler{

        @Override
        public void uncaughtException(Thread t, Throwable e) {
            System.out.println("caught:" + e);
        }
    }
    class HandlerThreadFactory implements ThreadFactory{

        @Override
        public Thread newThread(Runnable r) {
            System.out.println("create thread t");
            Thread thread = new Thread(r);
            System.out.println("set uncaughtException for t");
            //将自定义好的拦截方法设置进去
            thread.setUncaughtExceptionHandler(new MyUncaughExceptionHandler());
            return thread;
        }
    }

结果如下:

this is main
第一种
create thread t
set uncaughtException for t
other thread is running
------------------
caught:java.lang.RuntimeException: this is error!

第二种:通过默认方法,不去自定义工厂方法

每一个线程的异常处理相同,使用Thread的静态方法
1.自定义错误拦截方法

class MyUncaughExceptionHandler implements Thread.UncaughtExceptionHandler{

        @Override
        public void uncaughtException(Thread t, Throwable e) {
            System.out.println("caught:" + e);
        }
    }

2.实现子线程错误捕捉

public class test11_29 {
    public static void main(String[] args) {
        
        System.out.println("this is main");
        // //必须我们自己实现
        System.out.println("第二种");
        //通过默认方法进行实现
        //直接设置错误拦截器
        Thread.setDefaultUncaughtExceptionHandler(new MyUncaughExceptionHandler());
        //默认方法创建线程池
        ExecutorService newCachedThreadPool2 = Executors.newCachedThreadPool();
        //启动子线程
        newCachedThreadPool2.execute(new myExceptionRunner());
        newCachedThreadPool2.shutdown();
    }
    
}

3.汇总代码如下

public class test11_29 {
        public static void main(String[] args) {
            System.out.println("this is main");
        // //必须我们自己实现
        System.out.println("第二种");
        //通过默认方法进行实现
        //直接设置错误拦截器
        Thread.setDefaultUncaughtExceptionHandler(new MyUncaughExceptionHandler());
        //默认方法创建线程池
        ExecutorService newCachedThreadPool2 = Executors.newCachedThreadPool();
        //启动子线程
        newCachedThreadPool2.execute(new myExceptionRunner());
        newCachedThreadPool2.shutdown();
        }
    }
    class myExceptionRunner implements Runnable{
        @Override
        public void run() {
            System.out.println("other thread is running");
            System.out.println("------------------");
            throw new RuntimeException("this is error!");
        }
    }
    class MyUncaughExceptionHandler implements Thread.UncaughtExceptionHandler{

        @Override
        public void uncaughtException(Thread t, Throwable e) {
            System.out.println("caught:" + e);
        }
    }

运行结果如下

this is main
第二种
other thread is running
------------------
caught:java.lang.RuntimeException: this is error!

总结

主线程无法获取子线程的错误,因此我们需要自定义去实现相关子线程的错误拦截方法
第一种:
设置工厂方法,将拦截方法在工厂方法创建线程时设置进去
第二种:
在主线程中直接利用Thread.setDefaultUncaughtExceptionHandler(new MyUncaughExceptionHandler());设置错误拦截方法

共同点是都会使用 Thread.setDefaultUncaughtExceptionHandler(new MyUncaughExceptionHandler()); 方法去设置错误拦截方法,只不过是创建方式不同,应用场景不同。

比之上述方法,还有一种编程上的处理方式可以借鉴,即,有时候主线程的调用方可能只是想知道子线程执行过程中发生过哪些异常,而不一定会处理或是立即处理,那么发起子线程的方法可以把子线程抛出的异常实例收集起来作为一个Exception的List返回给调用方,由调用方来根据异常情况决定如何应对。不过要特别注意的是,此时子线程早以终结。

线程设计的理念:“线程的问题应该线程自己本身来解决,而不要委托到外部。”

参考来源:
Java中主线程如何捕获子线程抛出的异常
java主线程捕获子线程中的异常