美文网首页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