线程异常的处理顺序

为了方便管理一批线程,我们使用ThreadGroup来表示线程组,通过它对一批线程进行分类管理
使用方法:

Thread group = new ThreadGroup("group");
Thread thread = new Thread(gourp, ()->{..});

即thread除了Thread(Runable)这个构造方法外,还有个Thread(ThreadGroup, Runnable)构造方法

Q:
在线程A中创建线程B, 他们属于同一个线程组吗
A:
是的

线程组的一大作用是对同一个组线程进行统一的异常捕捉处理,避免每次新建线程时都要重新去setUncaghtExceptionHandler。即线程组自身可以实现一个uncaughtException方法。

ThreadGroup group = new ThreadGroup("group") {
	@Override
	public void uncaughtException(Thread thread, Throwable throwable) {
		System.out.println(thread.getName() + throwable.getMessage());
		}
	};
}

线程如果抛出异常,且没有在线程内部被捕捉,那么此时线程异常的处理顺序是什么?
相信很多人都看过下面这段话,好多讲线程组的博客里都这样写:
(1)首先看看当前线程组(ThreadGroup)有没有父类的线程组,如果有,则使用父类的UncaughtException()方法。
(2)如果没有,就看线程是不是调用setUncaughtExceptionHandler()方法建立Thread.setUncaughtExceptionHandler实例。如果建立,直接使用它的UncaughtException()方法处理异常。
(3)如果上述都不成立就看这个异常是不是ThreadDead实例,如果是,什么都不做,如果不是,输出堆栈追踪信息(printStackTrace)。

来源:
https://blog.csdn.net/qq_43073128/article/details/90597006
https://blog.csdn.net/qq_43073128/article/details/88280469

好,别急着记,先看一下下面的题目,问输出什么:
Q:

// 父类线程组
static class GroupFather extends ThreadGroup {
    public GroupFather(String name) {
        super(name);
    }
    @Override
    public void uncaughtException(Thread thread, Throwable throwable) {
        System.out.println("groupFather=" + throwable.getMessage());
    }
}
public static void main(String[] args) {
    // 子类线程组
    GroupFather groupSon = new GroupFather("groupSon") {
        @Override
        public void uncaughtException(Thread thread, Throwable throwable) {
            System.out.println("groupSon=" + throwable.getMessage());
        }
    };
 
 
    Thread thread1 = new Thread(groupSon, ()->{
        throw new RuntimeException("我异常了");
    });
    thread1.start();
}

A:
一看(1),那是不是应该输出groupFather?
错错错,输出的是groupSon这句话在很多地方能看到,但没有去实践过看过源码的人就会这句话被误导。
实际上父线程组不是指类继承关系上的线程组,而是指下面这样的:
image.png
即指的是构造关系的有父子关系。
如果子类的threadGroup没有去实现uncaughtException方法,那么就会去构造参数里指定的父线程组去调用方法。

Q: 那我改成构造关系上的父子关系,下面输出什么?

public static void main(String[] args) {
    // 父线程组
    ThreadGroup groupFather = new ThreadGroup("groupFather") {
        @Override
        public void uncaughtException(Thread thread, Throwable throwable) {
            System.out.println("groupFather=" + throwable.getMessage());
        }
    };
 
    // 子线程组,把groupFather作为parent参数
    ThreadGroup groupSon = new ThreadGroup(groupFather, "groupSon") {
        @Override
        public void uncaughtException(Thread thread, Throwable throwable) {
            System.out.println("groupSon=" + throwable.getMessage());
        }
    };
 
    Thread thread1 = new Thread(groupSon, ()->{
        throw new RuntimeException("我异常了");
    });
 
    thread1.start();
}

A:
答案输出
image.png
即只要子线程组有实现过,则会用子线程组里的方法,而不是直接去找的父线程组!

Q:
如果我让自己做set捕捉器的操作呢?那下面这个输出什么?

public static void main(String[] args) {
    // 父线程组
    ThreadGroup group = new ThreadGroup("group") {
        @Override
        public void uncaughtException(Thread thread, Throwable throwable) {
            System.out.println("group=" + throwable.getMessage());
        }
    };
 
    // 建一个线程,在线程组内
    Thread thread1 = new Thread(group, () -> {
        throw new RuntimeException("我异常了");
    });
 
    // 自己设置setUncaughtExceptionHandler方法
    thread1.setUncaughtExceptionHandler((t, e) -> {
        System.out.println("no gourp:" + e.getMessage());
    });
 
    thread1.start();
}

A:
看之前的结论里,似乎是应该输出线程组的异常?
但是结果却输出的是:
image.png
也就是说,如果线程对自己特地执行过setUncaughtExceptionHandler,那么有优先对自己设置过的UncaughtExceptionHandler做处理。

那难道第(2)点这个是错的吗?确实错了,实际上第二点应该指的是全局Thread的默认捕捉器,注意是全局的
实际上那段话出自ThreadGroup里uncaughtException的源码:
image.png
这里就解释了之前的那三点,但是该代码中没考虑线程自身设置了捕捉器

所以修改一下之前的总结一下线程的实际异常抛出判断逻辑:

如果线程自身有进行过setUncaughtExceptionHandler,则使用自己设置的按个。
如果没设置过,则看一下没有线程组。并按照以下逻辑判断:
如果线程组有覆写过uncaughtException,则用覆写过的uncaughtException
如果线程组没有覆写过,则去找父线程组(注意是构造体上的概念)的uncaughtException方法。
如果线程组以及父类都没覆写过uncaughtException, 则判断是否用Thread.setDefaultUncaughtExceptionHandler(xxx)去设置全局的默认捕捉器,有的话则用全局默认
如果不是ThreadDeath线程, 则只打印堆栈。
如果是ThreadDeath线程,那么就什么也不处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值