多线程-异常

1. 介绍

线程不允许抛出未捕获的checked exception

就是说各个线程需要自己把自己的checked exception处理掉

JVM的这种设计源自于这样一种理念:“线程是独立执行的代码片断,线程的问题应该由线程自己来解决,而不要委托到外部。”基于这样的设计理念,在Java中,线程方法的异常(无论是checked还是unchecked exception),都应该在线程代码边界之内(run方法内)进行try catch并处理掉。换句话说, 我们不能捕获从线程中逃逸的异常。

2. 未捕获的异常哪去了

一个异常被抛出后,如果没有被捕获处理,则会一直向上抛。

异常一旦被Thread.run() 抛出后,就不能在程序中对异常进行捕获,最终只能由JVM捕获。

2.1. 不处理异常

public static void main(String[] args) {
    new Thread(new Runnable() {
        @Override
        public void run() {
            int i = 1 / 0;//发生异常
        }
    }).start();
}

Exception in thread "Thread-0" java.lang.ArithmeticException: / by zero

   at thread.thread.thread.Uncaught$1.run

   at java.lang.Thread.run

2.2. 尝试对异常进行捕获

 public static void main(String[] args) {
        try {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    int i = 1 / 0;//发生异常
                }
            }).start();
        } catch (Exception e) {
            System.out.println("捕获线程抛出的异常!");
        }
    }

Exception in thread "Thread-0" java.lang.ArithmeticException: / by zero

   at thread.thread.thread.Uncaught$1.run

   at java.lang.Thread.run

3. JVM如何处理线程中抛出的异常

public void uncaughtException(Thread t, Throwable e) {
  if (parent != null) { // 父级优先处理
    parent.uncaughtException(t, e);
  } else {
    Thread.UncaughtExceptionHandler ueh = Thread.getDefaultUncaughtExceptionHandler();
    if (ueh != null) {
      ueh.uncaughtException(t, e);
    } else if (!(e instanceof ThreadDeath)) { // 没有配置handler时,默认直接打印到控制台
      System.err.print("Exception in thread \""
                       + t.getName() + "\" ");
      e.printStackTrace(System.err);
    }
  }
}
  • 查看Thread类的源码,我们可以看到有个dispatchUncaughtException方法,此方法就是用来处理线程中抛出的异常的。JVM会调用dispatchUncaughtException方法来寻找异常处理(UncaughtExceptionHandler),处理异常。
  • UncaughtExceptionHandler必须显示的设置,否则默认为null。若为null,则使用线程默认的handler,即该线程所属的ThreadGroup。ThreadGroup自身就是一个handler,查看ThreadGroup的源码就可以发现,ThreadGroup实现了Thread.UncaughtExceptionHandler接口,并实现了默认的处理方法。默认的未捕获异常处理器处理时,会调用 System.err 进行输出,也就是直接打印到控制台了。

4. 为线程 设置异常handle

public class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {

  private final String name;

  public MyUncaughtExceptionHandler(String name) {
    this.name = name;
  }

  @Override
  public void uncaughtException(Thread t, Throwable e) {
    Logger logger = Logger.getAnonymousLogger();
    logger.log(Level.WARNING, "线程异常,终止啦" + t.getName());
    e.printStackTrace();
    System.out.println(name + "捕获了异常" + t.getName() + "异常");
  }
}
Thread thread = new Thread(() -> {
    try {
        int i = 1 / 0;
    } catch (Exception e) {
        throw new RuntimeException("参数错误", e);
    }
});
thread.setUncaughtExceptionHandler( new MyUncaughtExceptionHandler("数据处理"));
thread.start();

1月 07, 2023 11:00:02 上午 com.du.exception.MyUncaughtExceptionHandler uncaughtException

警告: 线程异常,终止啦Thread-0

java.lang.RuntimeException: 参数错误

5. 为所有的线程设置

Thread的静态方法 setDefaultUncaughtExceptionHandler

6. future

并不是说一产生异常就抛出,直到我们get执行时,才会抛出

7. 线程组

public class ThreadGroupDemo {

    public static class MyThreadGroup extends ThreadGroup{
        public MyThreadGroup(String name) {
            super(name);
        }

        //重写uncaughtException方法
        @Override
        public void uncaughtException(Thread t, Throwable e) {
            //以下代码为捕获到异常之后的处理
            //super.uncaughtException(t, e);
            System.out.println("捕获到抛出异常线程:" + t.getName());
            System.out.println("捕获到异常信息:" + e.getMessage());
            //如果捕获到线程组内线程的异常,则终止线程组内所有线程
            this.interrupt();
        }
    }

    public static class MyThread extends Thread{
        private String num;
        public MyThread(ThreadGroup threadGroup, String name, String num) {
            super(threadGroup, name);
            this.num = num;
        }

        @Override
        public void run()  {
            try {
                int numInt = Integer.valueOf(num);
                System.out.println("死循环中"+Thread.currentThread().getName());
                while (!Thread.currentThread().isInterrupted()){
                }
                System.out.println("死循环结束"+Thread.currentThread().getName());
            } catch (Exception e) {
                //抛出运行时异常:子线程抛出异常
                throw new RuntimeException(Thread.currentThread().getName()+"子线程抛出异常");
            }
        }
    }

    public static void main(String[] args) {
        MyThreadGroup myThreadGroup = new MyThreadGroup("我的线程组");
        MyThread[] myThreads = new MyThread[5];
        for(int i = 0; i<5; i++){
            MyThread myThread = new MyThread(myThreadGroup,"线程"+i,String.valueOf(i));
            myThreads[i] = myThread;
        }
        for(MyThread myThread : myThreads){
            myThread.start();
        }
        //唯一的异常线程:我是异常线程
      //  MyThread myThread1 = new MyThread(myThreadGroup,"我是异常线程","a");
       // myThread1.start();
    }
}
  • 11
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值