我的原则:先会用再说,内部慢慢来。
学以致用,根据场景学源码
一、前言
1.1 架构
1.2 ThreadGroup 能干嘛?
- 能用来操作group下面的所有线程,比如全部打断 interrupt
- 可以进行链路监控,例如监控某个 group 下面,当前存在多少活跃线程数目。
- 统一捕获该group下线程抛出的异常。
1.3 ThreadGroup 常用的方法
方法 | 描述 |
---|---|
int activeCount() | 查看组内部 thread 活跃数量(包括子group) |
int activeGroupCount() | 查看组内部 group 活跃数量(包括子group) |
void list() | 打印出group下的所有线程信息(包括子group) |
void destroy() | 摧毁线程(包括子group) |
boolean isDestroyed() | 查看该 group 有没有被摧毁 |
int enumerate(Thread[] list) | copy 某个group 下面的活跃 thread |
ThreadGroup getParent() | 找爸爸 |
void interrupt() | 打断这个 group 下面的所有 thread |
boolean isDaemon() | 看下是否是幽灵线程 |
void setDaemon(boolean daemon) | 设置幽灵线程 |
二、实战
2.1 实战一 :验证 Count
- 本demo验证了activeCount、activeGroupCount、list、getParent、interrupt 等一系列方法
- 代码流程如下:
- 创建 group1
- group1 先创建thread1,2,3 ,由于没start,activeCount得到的的结果是 0
- thread1,2,3 启动起来(暂时不关闭),activeCount得到的的结果是 3
- 插入 thread4, 启动起来,activeCount得到的的结果是 4
- thread4 线程跑完,activeCount得到的的结果是 3
- 创建group2(传入 group1)
- 打印出 group2,group1,还有顶级 main 的list信息
- interrupt 测试打断,诶,虽然main线程跑到最后了,打那是发现scanner.nextLine() 由于内部的死循环while,无法被打断。
public static void testGroupListAndSize() throws Exception {
ThreadGroup group1 = new ThreadGroup("Group1");
Thread t1 = new Thread(group1, () -> {
try {
Scanner sc = new Scanner(System.in);
System.out.println("点击任意键唤醒线程 ...");
sc.nextLine();
} catch (Exception e) {
System.out.println("t1 被打断啦 ...");
}
});
Thread t2 = new Thread(group1, () -> {
try {
Scanner sc = new Scanner(System.in);
System.out.println("点击任意键唤醒线程 ...");
sc.nextLine();
} catch (Exception e) {
System.out.println("t2 被打断啦 ...");
}
});
Thread t3 = new Thread(group1, () -> {
try {
Scanner sc = new Scanner(System.in);
System.out.println("点击任意键唤醒线程 ...");
sc.nextLine();
} catch (Exception e) {
System.out.println("t3 被打断啦 ...");
}
});
/*
1. 线程未启动,未注册到 group 里面去
*/
group1.list();
System.out.println("group1.size -> " + group1.activeCount());
Thread.sleep(1000);
System.out.println("----");
/*
2. 启动线程
*/
t1.start();
t2.start();
t3.start();
Thread.sleep(1000);
group1.list();
System.out.println("group1.size -> " + group1.activeCount());
System.out.println("----");
System.out.println("==== 启动 thread4,验证 activeCount 含义(只返回活跃数量) ====");
/*
启动 thread4,验证 activeCount 含义(只返回活跃数量)
*/
Thread t4 = new Thread(group1, () -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("t4 被打断啦 ...");
}
});
t4.start();
group1.list();
System.out.println("group1.size -> " + group1.activeCount());
System.out.println("----");
Thread.sleep(2000);
System.out.println(" === 2s 后 ,t4 已经down ===");
group1.list();
System.out.println("group1.size -> " + group1.activeCount());
System.out.println("----");
Thread.sleep(1000);
System.out.println("==== 创建子group ====");
ThreadGroup group2 = new ThreadGroup(group1,"Group2");
Thread t21 = new Thread(group2, () -> {
try {
Scanner sc = new Scanner(System.in);
System.out.println("点击任意键唤醒线程 ...");
sc.nextLine();
} catch (Exception e) {
System.out.println("t21 被打断啦 ...");
}
});
t21.start();
Thread.sleep(1000);
System.out.println("遍历下 group1 ,观察是否加入了...");
group1.list();
System.out.println("遍历下 group2 ...");
group2.list();
System.out.println("最后遍历下最牛逼的 group-main ...");
Thread.currentThread().getThreadGroup().list();
Thread.sleep(1000);
/*
interrupt
*/
System.out.println("=== 最后打断全部线程 ===");
// group1.destroy();
group1.interrupt();
/*
sc.nextLine(); 太牛逼了,里面有死循环,听不到外面的打断
*/
System.out.println("main thread end ...");
}
- 输出:
java.lang.ThreadGroup[name=Group1,maxpri=10]
group1.size -> 0
----
点击任意键唤醒线程 ...
点击任意键唤醒线程 ...
点击任意键唤醒线程 ...
java.lang.ThreadGroup[name=Group1,maxpri=10]
Thread[Thread-0,5,Group1]
Thread[Thread-1,5,Group1]
Thread[Thread-2,5,Group1]
group1.size -> 3
----
==== 启动 thread4,验证 activeCount 含义(只返回活跃数量) ====
java.lang.ThreadGroup[name=Group1,maxpri=10]
Thread[Thread-0,5,Group1]
Thread[Thread-1,5,Group1]
Thread[Thread-2,5,Group1]
Thread[Thread-3,5,Group1]
group1.size -> 4
----
=== 2s 后 ,t4 已经down ===
java.lang.ThreadGroup[name=Group1,maxpri=10]
Thread[Thread-0,5,Group1]
Thread[Thread-1,5,Group1]
Thread[Thread-2,5,Group1]
group1.size -> 3
----
==== 创建子group ====
点击任意键唤醒线程 ...
遍历下 group1 ,观察是否加入了...
java.lang.ThreadGroup[name=Group1,maxpri=10]
Thread[Thread-0,5,Group1]
Thread[Thread-1,5,Group1]
Thread[Thread-2,5,Group1]
java.lang.ThreadGroup[name=Group2,maxpri=10]
Thread[Thread-4,5,Group2]
遍历下 group2 ...
java.lang.ThreadGroup[name=Group2,maxpri=10]
Thread[Thread-4,5,Group2]
最后遍历下最牛逼的 group-main ...
java.lang.ThreadGroup[name=main,maxpri=10]
Thread[main,5,main]
Thread[Monitor Ctrl-Break,5,main]
java.lang.ThreadGroup[name=Group1,maxpri=10]
Thread[Thread-0,5,Group1]
Thread[Thread-1,5,Group1]
Thread[Thread-2,5,Group1]
java.lang.ThreadGroup[name=Group2,maxpri=10]
Thread[Thread-4,5,Group2]
=== 最后打断全部线程 ===
main thread end ...
...
注意,这个地方子线程没有中断!!!一直跑着!!!
2.2 实战二 :验证 Interrupt
- 注意,上面这个程序,子线程没有中断!!!一直跑着!!!
- 由于上面有bug,我们重新验证下 interrupt 方法
Thread t1 = new Thread(group1, () -> {
try {
Thread.sleep(10000);
} catch (Exception e) {
System.out.println("t1 被打断啦 ...");
}
});
- 改动一下线程的内部实现,其余地方简化一下
public static void testInterrupte() throws Exception {
ThreadGroup group1 = new ThreadGroup("Group1");
Thread t1 = new Thread(group1, () -> {
try {
Thread.sleep(10000);
} catch (Exception e) {
System.out.println("t1 被打断啦 ...");
}
});
Thread t2 = new Thread(group1, () -> {
try {
Thread.sleep(10000);
} catch (Exception e) {
System.out.println("t2 被打断啦 ...");
}
});
Thread t3 = new Thread(group1, () -> {
try {
Thread.sleep(10000);
} catch (Exception e) {
System.out.println("t3 被打断啦 ...");
}
});
/*
2. 启动线程
*/
t1.start();
t2.start();
t3.start();
Thread.sleep(1000);
group1.list();
System.out.println("group1.size -> " + group1.activeCount());
System.out.println("----");
System.out.println("==== 启动 thread4,验证 activeCount 含义(只返回活跃数量) ====");
Thread.sleep(1000);
System.out.println("==== 创建子group ====");
ThreadGroup group2 = new ThreadGroup(group1,"Group2");
Thread t21 = new Thread(group2, () -> {
try {
Thread.sleep(1000);
} catch (Exception e) {
System.out.println("t21 被打断啦 ...");
}
});
t21.start();
Thread.sleep(1000);
System.out.println("遍历下 group1 ,观察是否加入了...");
group1.list();
System.out.println("遍历下 group2 ...");
group2.list();
System.out.println("最后遍历下最牛逼的 group-main ...");
Thread.currentThread().getThreadGroup().list();
Thread.sleep(1000);
/*
interrupt
*/
System.out.println("=== 最后打断全部线程 ===");
// group1.destroy();
group1.interrupt();
System.out.println("main thread end ...");
}
输出:
java.lang.ThreadGroup[name=Group1,maxpri=10]
Thread[Thread-0,5,Group1]
Thread[Thread-1,5,Group1]
Thread[Thread-2,5,Group1]
group1.size -> 3
----
==== 启动 thread4,验证 activeCount 含义(只返回活跃数量) ====
==== 创建子group ====
遍历下 group1 ,观察是否加入了...
java.lang.ThreadGroup[name=Group1,maxpri=10]
Thread[Thread-0,5,Group1]
Thread[Thread-1,5,Group1]
Thread[Thread-2,5,Group1]
java.lang.ThreadGroup[name=Group2,maxpri=10]
Thread[Thread-3,5,Group2]
遍历下 group2 ...
java.lang.ThreadGroup[name=Group2,maxpri=10]
Thread[Thread-3,5,Group2]
最后遍历下最牛逼的 group-main ...
java.lang.ThreadGroup[name=main,maxpri=10]
Thread[main,5,main]
Thread[Monitor Ctrl-Break,5,main]
java.lang.ThreadGroup[name=Group1,maxpri=10]
Thread[Thread-0,5,Group1]
Thread[Thread-1,5,Group1]
Thread[Thread-2,5,Group1]
java.lang.ThreadGroup[name=Group2,maxpri=10]
Thread[Thread-3,5,Group2]
=== 最后打断全部线程 ===
main thread end ...
t1 被打断啦 ...
t2 被打断啦 ...
t3 被打断啦 ...
JVM跑完全程,无任何地方卡住。
2.3 实战三 :验证 enumerate
- 代码流程如下:
- 测试 enumerate(Thread list[]) ,复制group下面的线程
- 测试 enumerate(ThreadGroup list[]) ,复制整个group
- 测试发现是浅复制
public static void testCopy() throws Exception {
ThreadGroup group1 = new ThreadGroup("Group1");
System.out.println("----");
Thread t1 = new Thread(group1, () -> {
Scanner sc = new Scanner(System.in);
System.out.println("点击任意键唤醒线程 ...");
sc.nextLine();
});
Thread t2 = new Thread(group1, () -> {
Scanner sc = new Scanner(System.in);
System.out.println("点击任意键唤醒线程 ...");
sc.nextLine();
});
Thread t3 = new Thread(group1, () -> {
Scanner sc = new Scanner(System.in);
System.out.println("点击任意键唤醒线程 ...");
sc.nextLine();
});
group1.list();
System.out.println("group1.size -> " + group1.activeCount());
Thread.sleep(1000);
System.out.println("----");
t1.start();
t2.start();
t3.start();
/*
2. 开始 copy ,保留原 group
复制threads
*/
System.out.println("=== 2. begin to copy === ");
Thread[] threads = new Thread[group1.activeCount()];
group1.enumerate(threads);
// 上面仅仅是复制,没有启动。
System.out.println("group1.size -> " + group1.activeCount());
System.out.println("----");
Arrays.stream(threads).forEach(System.out::println);
Thread.sleep(1000);
System.out.println(threads[0] == t1);
System.out.println(threads[1] == t2);
System.out.println(threads[2] == t3);
System.out.println("threads 复制 enumerate 仅仅是浅复制 ...");
System.out.println("group1.size -> " + group1.activeCount());
System.out.println("----");
Thread.sleep(1000);
/*
复制 group
*/
System.out.println("=== 第二波复制 ===");
System.out.println("Thread.currentThread().getThreadGroup().activeGroupCount() -> " +
Thread.currentThread().getThreadGroup().activeGroupCount());
Thread.currentThread().getThreadGroup().list();
ThreadGroup[] groups = new ThreadGroup[Thread.currentThread().getThreadGroup().activeGroupCount()];
Thread.currentThread().getThreadGroup().enumerate(groups);
// 上面仅仅是复制,没有启动。
System.out.println("----");
Thread.sleep(1000);
System.out.println("=== 复制完了 ===");
for (ThreadGroup group :
groups) {
group.list();
}
System.out.println("groups[0] == group1 ? " + (groups[0] == group1));
System.out.println("group 复制 enumerate 仅仅是浅复制 ...");
System.out.println("group1.size -> " + group1.activeCount());
System.out.println("----");
}
输出:
----
java.lang.ThreadGroup[name=Group1,maxpri=10]
group1.size -> 0
----
=== 2. begin to copy ===
group1.size -> 3
----
Thread[Thread-0,5,Group1]
Thread[Thread-1,5,Group1]
Thread[Thread-2,5,Group1]
点击任意键唤醒线程 ...
点击任意键唤醒线程 ...
点击任意键唤醒线程 ...
true
true
true
enumerate 仅仅是浅复制 ...
group1.size -> 3
----
=== 第二波复制 ===
Thread.currentThread().getThreadGroup().activeGroupCount() -> 1
java.lang.ThreadGroup[name=main,maxpri=10]
Thread[main,5,main]
Thread[Monitor Ctrl-Break,5,main]
java.lang.ThreadGroup[name=Group1,maxpri=10]
Thread[Thread-0,5,Group1]
Thread[Thread-1,5,Group1]
Thread[Thread-2,5,Group1]
----
=== 复制完了 ===
java.lang.ThreadGroup[name=Group1,maxpri=10]
Thread[Thread-0,5,Group1]
Thread[Thread-1,5,Group1]
Thread[Thread-2,5,Group1]
groups[0] == group1 ? true
enumerate 仅仅是浅复制 ...
group1.size -> 3
----
...
(输入任意键,线程结束)
- 结论:浅层复制,只是引用复制。
2.4 实战四:测试默认 ThreadGroup
- 代码
public static void test01() throws Exception {
print();
new Thread(() -> print(),"A1").start();
new Thread(() -> new Thread(() -> print(),"B2").start(),"B1").start();
}
public static void print(){
System.out.println("currentThread -> " + Thread.currentThread() + ",group -> " + Thread.currentThread().getThreadGroup());
}
- 输出
currentThread -> Thread[main,5,main],group -> java.lang.ThreadGroup[name=main,maxpri=10]
currentThread -> Thread[A1,5,main],group -> java.lang.ThreadGroup[name=main,maxpri=10]
currentThread -> Thread[B2,5,main],group -> java.lang.ThreadGroup[name=main,maxpri=10]
- 结论: 不指定 group 的情况下,deamon 、priority 参数随父 thread。
2.5 实战五:测试 Exception 捕获
- 代码
public static void testCatchException() throws Exception{
ThreadGroup g1 = new ThreadGroup("ThreadGroup");
Thread t1 = new Thread(g1, () -> { throw new RuntimeException(Thread.currentThread() + "自定义的一个RuntimeException...");});
t1.start();
Thread.sleep(1000);
CatchExceptinoThreadGroup g2 = new CatchExceptinoThreadGroup("CatchExceptionThreadGroup");
Thread t2 = new Thread(g2, () -> { throw new RuntimeException(Thread.currentThread() + "自定义的一个RuntimeException...");});
t2.start();
}
public static class CatchExceptinoThreadGroup extends ThreadGroup{
public CatchExceptinoThreadGroup(String name) {
super(name);
}
public CatchExceptinoThreadGroup(ThreadGroup parent, String name) {
super(parent, name);
}
@Override
public void uncaughtException(Thread t, Throwable e) {
// 这里可以写 if else 处理各种各样的异常
if(e instanceof RuntimeException){
System.out.println("### CatchExceptinoThreadGroup catch " + e);
}
}
}
- 输出
Exception in thread "Thread-0" java.lang.RuntimeException: Thread[Thread-0,5,ThreadGroup]自定义的一个RuntimeException...
at indi.sword.util.basic.Thread._06_01_TestThreadGroup.lambda$testCatchException$15(_06_01_TestThreadGroup.java:291)
at java.lang.Thread.run(Thread.java:748)
### CatchExceptinoThreadGroup catch java.lang.RuntimeException: Thread[Thread-1,5,CatchExceptionThreadGroup]自定义的一个RuntimeException...
- 解析
- g1 线程组(默认组),group内线程抛出异常,无法在外头捕获
- g2 线程组 (重写组),group内线程抛出异常,被外层捕获到了
- 结论:可以重写 uncaughtException 方法来进行异常的捕获。关于 Thread.UncaughtExceptionHandler 下一章节进行讲解。
三、源码剖析
3.1 Class 类初始化
3.1.1 demo
Thread t1 = new Thread(group1, () -> {
Scanner sc = new Scanner(System.in);
System.out.println("点击任意键唤醒线程 ...");
sc.nextLine();
});
3.1.2 Thread 构造方法
- java.lang.Thread#Thread(java.lang.Runnable, java.lang.String)
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
3.1.3 init 方法
- java.lang.Thread#init(java.lang.ThreadGroup, java.lang.Runnable, java.lang.String, long)
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null, true);
}
- java.lang.Thread#init(java.lang.ThreadGroup, java.lang.Runnable, java.lang.String, long, java.security.AccessControlContext, boolean)
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
//当前线程就是该线程的父线程
Thread parent = currentThread();
//获取系统的security
SecurityManager security = System.getSecurityManager();
// 如果没有传入group
if (g == null) {
/* Determine if it's an applet or not */
/* If there is a security manager, ask the security manager
what to do. */
//security不为null时,线程所在group为security的group
if (security != null) {
g = security.getThreadGroup();
}
/* If the security doesn't have a strong opinion of the matter
use the parent thread group. */
//security为null时,直接使用父线程的group
if (g == null) {
g = parent.getThreadGroup();
}
}
/* checkAccess regardless of whether or not threadgroup is
explicitly passed in. */
g.checkAccess();
/*
* Do we have the required permissions?
*/
//授权
if (security != null) {
if (isCCLOverridden(getClass())) {
security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
}
g.addUnstarted();
this.group = g;
//将守护线程、优先级等设置为父线程的对应属性
this.daemon = parent.isDaemon();
this.priority = parent.getPriority();
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext =
acc != null ? acc : AccessController.getContext();
this.target = target;
setPriority(priority);
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
//创建线程共享变量副本
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
/* Set thread ID */
//分配线程id
tid = nextThreadID();
}
分析:
- 若 group 没有指定,那么使用的是父线程的 group ,daemon 与 priority 参数都跟随父类。
- if (inheritThreadLocals && parent.inheritableThreadLocals != null) 这个具体看上一篇【线程】InheritableThreadLocal 剖析 (十六)
3.2 activeCount 方法
- java.lang.ThreadGroup#activeCount
- 活跃 thread 数
public int activeCount() {
int result;
// Snapshot sub-group data so we don't hold this lock
// while our children are computing.
int ngroupsSnapshot;
ThreadGroup[] groupsSnapshot;
synchronized (this) {
if (destroyed) {
return 0;
}
result = nthreads; // 获取线程数
ngroupsSnapshot = ngroups;
if (groups != null) {
groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
} else {
groupsSnapshot = null;
}
}
for (int i = 0 ; i < ngroupsSnapshot ; i++) {
result += groupsSnapshot[i].activeCount();
}
return result;
}
- 解析:
- synchronized 方法块,确保线程安全
- 递归统计。(result = nthreads; // 获取线程数)
3.3 activeGroupCount 方法
- java.lang.ThreadGroup#activeGroupCount
public int activeGroupCount() {
int ngroupsSnapshot;
ThreadGroup[] groupsSnapshot;
synchronized (this) {
if (destroyed) {
return 0;
}
ngroupsSnapshot = ngroups;
if (groups != null) {
groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
} else {
groupsSnapshot = null;
}
}
int n = ngroupsSnapshot; // 获取 group 数
for (int i = 0 ; i < ngroupsSnapshot ; i++) {
n += groupsSnapshot[i].activeGroupCount(); // 递归
}
return n;
}
- 解析:
- synchronized 方法块,确保线程安全
- 递归统计。(int n = ngroupsSnapshot; // 获取 group 数 )
3.4 list 方法
- 用于打印操作
- java.lang.ThreadGroup#list()
public void list() {
list(System.out, 0);
}
- java.lang.ThreadGroup#list(java.io.PrintStream, int) 方法
void list(PrintStream out, int indent) {
int ngroupsSnapshot;
ThreadGroup[] groupsSnapshot;
synchronized (this) {
for (int j = 0 ; j < indent ; j++) {
out.print(" ");
}
out.println(this);
indent += 4;
for (int i = 0 ; i < nthreads ; i++) {
for (int j = 0 ; j < indent ; j++) {
out.print(" ");
}
out.println(threads[i]);
}
ngroupsSnapshot = ngroups;
if (groups != null) {
groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
} else {
groupsSnapshot = null;
}
}
for (int i = 0 ; i < ngroupsSnapshot ; i++) {
groupsSnapshot[i].list(out, indent); //递归
}
}
- 解析:
- synchronized 方法块,确保线程安全
- 参数 PrintStream 指定输出流,默认控制台 (System.out,)
- 参数 indent ,打印前置多少个空格,目的是为了层级显示。
- groupsSnapshot[i].list(out, indent); 递归进行 list 打印操作。
3.5 enumerate 方法
- java.lang.ThreadGroup#enumerate(java.lang.Thread[])
- 用于复制操作
public int enumerate(Thread list[]) {
checkAccess();
return enumerate(list, 0, true);
}
- java.lang.ThreadGroup#enumerate(java.lang.Thread[], int, boolean) 方法
private int enumerate(Thread list[], int n, boolean recurse) {
int ngroupsSnapshot = 0;
ThreadGroup[] groupsSnapshot = null;
synchronized (this) {
if (destroyed) {
return 0;
}
int nt = nthreads;
if (nt > list.length - n) {
nt = list.length - n;
}
for (int i = 0; i < nt; i++) {
if (threads[i].isAlive()) {
list[n++] = threads[i];
}
}
if (recurse) {
ngroupsSnapshot = ngroups;
if (groups != null) {
groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
} else {
groupsSnapshot = null;
}
}
}
if (recurse) {
for (int i = 0 ; i < ngroupsSnapshot ; i++) {
n = groupsSnapshot[i].enumerate(list, n, true); //递归
}
}
return n;
}
- 解析:
- synchronized 方法块,确保线程安全
- Arrays.copyOf 进行复制(最终调用Native方法 java.lang.System#arraycopy )
- groupsSnapshot[i].enumerate(list, n, true); 递归进行复制
3.6 interrupt 方法
- java.lang.ThreadGroup#interrupt
public final void interrupt() {
int ngroupsSnapshot;
ThreadGroup[] groupsSnapshot;
synchronized (this) {
checkAccess();
for (int i = 0 ; i < nthreads ; i++) {
threads[i].interrupt();
}
ngroupsSnapshot = ngroups;
if (groups != null) {
groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
} else {
groupsSnapshot = null;
}
}
for (int i = 0 ; i < ngroupsSnapshot ; i++) {
groupsSnapshot[i].interrupt(); //递归
}
}
- 解析:
- synchronized 方法块,确保线程安全
- groupsSnapshot[i].interrupt(); 内部递归调用 interrupt
3.7 uncaughtException 方法
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);
}
}
}
- 解析:
- 对于子Thread抛出的Exception,递归交由父 ThreadGroup 定义的 uncaughtException 方法来处理。
四、番外篇
下一章节:【线程】Thread.UncaughtExceptionHandler 实战与剖析 (十八)
上一章节:【线程】InheritableThreadLocal 剖析 (十六)