为了方便管理一批线程,我们使用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线程,那么就什么也不处理。