Java线程学习记录(九)——线程异常处理

线程的未捕获异常UncaughtException应该如何处理

为什么需要UncaughExceptionHandler

主线程可以轻松发现异常,子线程却不行
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();
    }
}

在这段代码的执行输出中,子线程确实抛出了异常,主线程也是不受影响。但是随着主线程的打印数据过多,开发人员可能会忽略掉(没有发现)这个子线程的异常。

子线程异常无法用传统方法捕获
不加try/catch的情况下
public class CantCatchDirectly implements Runnable {
    public static void main(String[] args) throws InterruptedException {
        new Thread(new CantCatchDirectly(), "线程-1").start();
        Thread.sleep(300);
        new Thread(new CantCatchDirectly(), "线程-2").start();
        Thread.sleep(300);
        new Thread(new CantCatchDirectly(), "线程-3").start();
        Thread.sleep(300);
        new Thread(new CantCatchDirectly(), "线程-4").start();
    }

    @Override
    public void run() {
        throw new RuntimeException();
    }
}

从输出中,4个线程确实抛出了异常

Exception in thread "线程-1" java.lang.RuntimeException
	at threadcoreknowledge.ubcaughtexception.CantCatchDirectly.run(CantCatchDirectly.java:27)
	at java.lang.Thread.run(Thread.java:748)
Exception in thread "线程-2" java.lang.RuntimeException
	at threadcoreknowledge.ubcaughtexception.CantCatchDirectly.run(CantCatchDirectly.java:27)
	at java.lang.Thread.run(Thread.java:748)
Exception in thread "线程-3" java.lang.RuntimeException
	at threadcoreknowledge.ubcaughtexception.CantCatchDirectly.run(CantCatchDirectly.java:27)
	at java.lang.Thread.run(Thread.java:748)
Exception in thread "线程-4" java.lang.RuntimeException
	at threadcoreknowledge.ubcaughtexception.CantCatchDirectly.run(CantCatchDirectly.java:27)
	at java.lang.Thread.run(Thread.java:748)
加了try/catch的情况下
public class CantCatchDirectly implements Runnable {
    public static void main(String[] args) throws InterruptedException {
        try {
            new Thread(new CantCatchDirectly(), "线程-1").start();
            Thread.sleep(300);
            new Thread(new CantCatchDirectly(), "线程-2").start();
            Thread.sleep(300);
            new Thread(new CantCatchDirectly(), "线程-3").start();
            Thread.sleep(300);
            new Thread(new CantCatchDirectly(), "线程-4").start();
        } catch (RuntimeException e) {
            System.out.println("抓住了异常");
        }
    }

    @Override
    public void run() {
        throw new RuntimeException();
    }
}

从输出中,并没有打印“抓住了异常”,那就是并没有被catch,这是为什么呢?
这是由于try/catch的部分是对于主线的,只有在启动线程的时候发生异常才会被catch,而这里的异常是在子线程中抛出的,所以try/catch没有捕获到。

Exception in thread "线程-1" java.lang.RuntimeException
	at threadcoreknowledge.ubcaughtexception.CantCatchDirectly.run(CantCatchDirectly.java:31)
	at java.lang.Thread.run(Thread.java:748)
Exception in thread "线程-2" java.lang.RuntimeException
	at threadcoreknowledge.ubcaughtexception.CantCatchDirectly.run(CantCatchDirectly.java:31)
	at java.lang.Thread.run(Thread.java:748)
Exception in thread "线程-3" java.lang.RuntimeException
	at threadcoreknowledge.ubcaughtexception.CantCatchDirectly.run(CantCatchDirectly.java:31)
	at java.lang.Thread.run(Thread.java:748)
Exception in thread "线程-4" java.lang.RuntimeException
	at threadcoreknowledge.ubcaughtexception.CantCatchDirectly.run(CantCatchDirectly.java:31)
	at java.lang.Thread.run(Thread.java:748)
不能直接捕获的后果、提高健壮性

对于子线程直接抛出异常,不进行捕获,那么他就是打印堆栈,子线程就是停止,那就没有人去查看我们的业务逻辑到底是出现了什么问题,所以需要用一个全局的Handler来进行处理(比如线程重启,或者通知开发者来介入查看)。

解决方案

手动在每个run方法里进行try/catch(不推荐)

在之前的代码基础上,在run方法中加入try/catch

public class CantCatchDirectly implements Runnable {
    public static void main(String[] args) throws InterruptedException {
        try {
            new Thread(new CantCatchDirectly(), "线程-1").start();
            Thread.sleep(300);
            new Thread(new CantCatchDirectly(), "线程-2").start();
            Thread.sleep(300);
            new Thread(new CantCatchDirectly(), "线程-3").start();
            Thread.sleep(300);
            new Thread(new CantCatchDirectly(), "线程-4").start();
        } catch (RuntimeException e) {
            System.out.println("抓住了异常");
        }
    }

    @Override
    public void run() {
        try {
            throw new RuntimeException();
        } catch (RuntimeException e) {
            System.out.println("抓住了异常");
        }
    }
}

确实抓出了异常,但是这样我们要为每一个类进行try/catch,而且有时候我们并不知道他异常的类是什么,这样就过于麻烦。

抓住了异常
抓住了异常
抓住了异常
抓住了异常
利用UncaughtExceptionHandler

UncaughtExceptionHandler接口代码

@FunctionalInterface
    public interface UncaughtExceptionHandler {
        /**
         * Method invoked when the given thread terminates due to the
         * given uncaught exception.
         * <p>Any exception thrown by this method will be ignored by the
         * Java Virtual Machine.
         * @param t the thread
         * @param e the exception
         */
        void uncaughtException(Thread t, Throwable e);
    }
异常处理器的调用策略

首先会检查有没有父线程,如果有的话就用父线程的方法,他采用的是递归的方法逐级去找,一直找到最顶层的ThreadGroup的处理器;如果没有则getDefaultUncaughtExceptionHandler(),如果取到不为空,就可以用我们实现的异常处理去处理,反之就输出异常堆栈。
这里就是需要我们自己去实现一个异常处理。

private final ThreadGroup parent;
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)) {
                System.err.print("Exception in thread \""
                                 + t.getName() + "\" ");
                e.printStackTrace(System.err);
            }
        }
    }
实现异常处理器
处理器实现
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(), e);
        System.out.println(name + "捕获了异常" + t.getName() + "异常" + e);
    }

}
处理器调用
public class UseOwnUncaughtExceptionHandler implements Runnable {

    public static void main(String[] args) throws InterruptedException {
        Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler("捕获器"));

        new Thread(new UseOwnUncaughtExceptionHandler(), "线程-1").start();
        Thread.sleep(300);
        new Thread(new UseOwnUncaughtExceptionHandler(), "线程-2").start();
        Thread.sleep(300);
        new Thread(new UseOwnUncaughtExceptionHandler(), "线程-3").start();
        Thread.sleep(300);
        new Thread(new UseOwnUncaughtExceptionHandler(), "线程-4").start();
    }

    @Override
    public void run() {
        throw new RuntimeException();

    }
}

输出

五月 24, 2020 7:42:17 下午 threadcoreknowledge.ubcaughtexception.MyUncaughtExceptionHandler uncaughtException
警告: 线程异常,终止啦线程-1
java.lang.RuntimeException
	at threadcoreknowledge.ubcaughtexception.UseOwnUncaughtExceptionHandler.run(UseOwnUncaughtExceptionHandler.java:24)
	at java.lang.Thread.run(Thread.java:748)

捕获器捕获了异常线程-1异常java.lang.RuntimeException
五月 24, 2020 7:42:17 下午 threadcoreknowledge.ubcaughtexception.MyUncaughtExceptionHandler uncaughtException
警告: 线程异常,终止啦线程-2
java.lang.RuntimeException
	at threadcoreknowledge.ubcaughtexception.UseOwnUncaughtExceptionHandler.run(UseOwnUncaughtExceptionHandler.java:24)
	at java.lang.Thread.run(Thread.java:748)

捕获器捕获了异常线程-2异常java.lang.RuntimeException
五月 24, 2020 7:42:18 下午 threadcoreknowledge.ubcaughtexception.MyUncaughtExceptionHandler uncaughtException
警告: 线程异常,终止啦线程-3
java.lang.RuntimeException
	at threadcoreknowledge.ubcaughtexception.UseOwnUncaughtExceptionHandler.run(UseOwnUncaughtExceptionHandler.java:24)
	at java.lang.Thread.run(Thread.java:748)

捕获器捕获了异常线程-3异常java.lang.RuntimeException
五月 24, 2020 7:42:18 下午 threadcoreknowledge.ubcaughtexception.MyUncaughtExceptionHandler uncaughtException
警告: 线程异常,终止啦线程-4
java.lang.RuntimeException
	at threadcoreknowledge.ubcaughtexception.UseOwnUncaughtExceptionHandler.run(UseOwnUncaughtExceptionHandler.java:24)
	at java.lang.Thread.run(Thread.java:748)

捕获器捕获了异常线程-4异常java.lang.RuntimeException
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值