美文网首页Java-多线程
Java并发编程 线程的未捕获UncaughtException

Java并发编程 线程的未捕获UncaughtException

作者: 香沙小熊 | 来源:发表于2020-12-24 11:09 被阅读0次

    1.为什么需要UncaughtExceptionHandler

    • 主线程可以轻松发现异常,子线程却不行
    public class ExceptionInChildThread implements Runnable {
    
        public static void main(String[] args) {
            new Thread(new ExceptionInChildThread()).start();
            for (int i = 0; i < 1000; i++) {
                System.out.println(i);
            }
        }
    
        @Override
        public void run() {
            throw new RuntimeException();
        }
    }
    
    0
    1
    ...省略
    827
    828
    Exception in thread "Thread-0" java.lang.RuntimeException
        at com.kpioneer.thread.threadcoreknowledge.uncaughtexception.ExceptionInChildThread.run(ExceptionInChildThread.java:17)
        at java.lang.Thread.run(Thread.java:748)
    829
    830
    ...省略
    

    子线程抛出的异常淹没在其它日志中,难以发现

    • 子线程异常无法用传统方法捕获
    public class CantCatchDirectly implements Runnable {
    
        public static void main(String[] args) throws InterruptedException {
            try {
                new Thread(new CantCatchDirectly(), "MyThread-1").start();
                Thread.sleep(300);
                new Thread(new CantCatchDirectly(), "MyThread-2").start();
                Thread.sleep(300);
                new Thread(new CantCatchDirectly(), "MyThread-3").start();
                Thread.sleep(300);
                new Thread(new CantCatchDirectly(), "MyThread-4").start();
            } catch (RuntimeException e) {
                System.out.println("main Caught Exception.");
            }
    
        }
    
        @Override
        public void run() {
            throw new RuntimeException();
        }
    }
    
    Exception in thread "MyThread-1" java.lang.RuntimeException
        at com.kpioneer.thread.threadcoreknowledge.uncaughtexception.CantCatchDirectly.run(CantCatchDirectly.java:30)
        at java.lang.Thread.run(Thread.java:748)
    Exception in thread "MyThread-2" java.lang.RuntimeException
        at com.kpioneer.thread.threadcoreknowledge.uncaughtexception.CantCatchDirectly.run(CantCatchDirectly.java:30)
        at java.lang.Thread.run(Thread.java:748)
    Exception in thread "MyThread-3" java.lang.RuntimeException
        at com.kpioneer.thread.threadcoreknowledge.uncaughtexception.CantCatchDirectly.run(CantCatchDirectly.java:30)
        at java.lang.Thread.run(Thread.java:748)
    Exception in thread "MyThread-4" java.lang.RuntimeException
        at com.kpioneer.thread.threadcoreknowledge.uncaughtexception.CantCatchDirectly.run(CantCatchDirectly.java:30)
        at java.lang.Thread.run(Thread.java:748)
    

    main Caught Exception. 并没有打印,同时线程停止。

    • 不能直接捕获的后果
      结果是子线程停止运行,打印堆栈信息,然而我们的业务逻辑没有去执行

    2.两种解决办法

    1. 手动在每个run方法里进行try catch(不推荐)
    public class CantCatchDirectly implements Runnable {
    
        public static void main(String[] args) throws InterruptedException {
            try {
                new Thread(new CantCatchDirectly(), "MyThread-1").start();
                Thread.sleep(300);
                new Thread(new CantCatchDirectly(), "MyThread-2").start();
                Thread.sleep(300);
                new Thread(new CantCatchDirectly(), "MyThread-3").start();
                Thread.sleep(300);
                new Thread(new CantCatchDirectly(), "MyThread-4").start();
            } catch (RuntimeException e) {
                System.out.println("main Caught Exception.");
            }
    
        }
    
        @Override
        public void run() {
            try {
                throw new RuntimeException();
            } catch (RuntimeException e) {
                System.out.println("Caught Exception.");
            }
        }
    }
    
    Caught Exception.
    Caught Exception.
    Caught Exception.
    Caught Exception.
    
    2. 利用UncaughtExceptionHandler(推荐)

    方法

    • UncaughtExceptionHandler接口
    • void uncaughtException(Thread t, Throwable e);
    /**
     * 描述:     自己的MyUncaughtExceptionHanlder
     */
    public class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
    
        private 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());
            System.out.println(name + "捕获了异常" + t.getName() + "异常");
        }
    }
    
    public class UseOwnUncaughtExceptionHandler implements Runnable {
    
        public static void main(String[] args) throws InterruptedException {
            Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler("捕获器1"));
    
            new Thread(new UseOwnUncaughtExceptionHandler(), "MyThread-1").start();
            Thread.sleep(300);
            new Thread(new UseOwnUncaughtExceptionHandler(), "MyThread-2").start();
            Thread.sleep(300);
            new Thread(new UseOwnUncaughtExceptionHandler(), "MyThread-3").start();
            Thread.sleep(300);
            new Thread(new UseOwnUncaughtExceptionHandler(), "MyThread-4").start();
        }
    
    
        @Override
        public void run() {
            throw new RuntimeException();
        }
    }
    
    十二月 24, 2020 10:49:44 上午 com.kpioneer.thread.threadcoreknowledge.uncaughtexception.MyUncaughtExceptionHandler uncaughtException
    警告: 线程异常,终止啦MyThread-1
    捕获器1捕获了异常MyThread-1异常
    十二月 24, 2020 10:49:44 上午 com.kpioneer.thread.threadcoreknowledge.uncaughtexception.MyUncaughtExceptionHandler uncaughtException
    警告: 线程异常,终止啦MyThread-2
    捕获器1捕获了异常MyThread-2异常
    十二月 24, 2020 10:49:44 上午 com.kpioneer.thread.threadcoreknowledge.uncaughtexception.MyUncaughtExceptionHandler uncaughtException
    警告: 线程异常,终止啦MyThread-3
    捕获器1捕获了异常MyThread-3异常
    十二月 24, 2020 10:49:45 上午 com.kpioneer.thread.threadcoreknowledge.uncaughtexception.MyUncaughtExceptionHandler uncaughtException
    警告: 线程异常,终止啦MyThread-4
    捕获器1捕获了异常MyThread-4异常
    
    
    注意:实际生产环境中 这些日志应该保存起来,直接通知告警系统,返回前端统一的描述。

    3.常见问题

    run方法是否可以向外抛出异常?如果抛出异常,线程的状态是怎么样的?
    不能向外抛出异常,如果抛出的是RuntimeException,线程会停止。
    线程中如何处理某个未处理异常?
    利用UncaughtExceptionHandler统一捕获异常

    特别感谢

    悟空

    相关文章

      网友评论

        本文标题:Java并发编程 线程的未捕获UncaughtException

        本文链接:https://www.haomeiwen.com/subject/touunktx.html