【线程】ThreadGroup 实战与剖析 (十七)

mac2022-06-30  27

我的原则:先会用再说,内部慢慢来。 学以致用,根据场景学源码

文章目录

一、前言1.1 架构1.2 ThreadGroup 能干嘛?1.3 ThreadGroup 常用的方法 二、实战2.1 实战一 :验证 Count2.2 实战二 :验证 Interrupt2.3 实战三 :验证 enumerate2.4 实战四:测试默认 ThreadGroup2.5 实战五:测试 Exception 捕获 三、源码剖析3.1 Class 类初始化3.1.1 demo3.1.2 Thread 构造方法3.1.3 init 方法 3.2 activeCount 方法3.3 activeGroupCount 方法3.4 list 方法3.5 enumerate 方法3.6 interrupt 方法3.7 uncaughtException 方法 四、番外篇


一、前言

1.1 架构

=== 点击查看top目录 ===

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 下面的活跃 threadThreadGroup getParent()找爸爸void interrupt()打断这个 group 下面的所有 threadboolean isDaemon()看下是否是幽灵线程void setDaemon(boolean daemon)设置幽灵线程

二、实战

2.1 实战一 :验证 Count

本demo验证了activeCount、activeGroupCount、list、getParent、interrupt 等一系列方法代码流程如下: 创建 group1group1 先创建thread1,2,3 ,由于没start,activeCount得到的的结果是 0thread1,2,3 启动起来(暂时不关闭),activeCount得到的的结果是 3插入 thread4, 启动起来,activeCount得到的的结果是 4thread4 线程跑完,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 ... ... 注意,这个地方子线程没有中断!!!一直跑着!!!

=== 点击查看top目录 ===

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跑完全程,无任何地方卡住。

=== 点击查看top目录 ===

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 ---- ... (输入任意键,线程结束) 结论:浅层复制,只是引用复制。

=== 点击查看top目录 ===

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。

=== 点击查看top目录 ===

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 下一章节进行讲解。

=== 点击查看top目录 ===

三、源码剖析

3.1 Class 类初始化

3.1.1 demo
Thread t1 = new Thread(group1, () -> { Scanner sc = new Scanner(System.in); System.out.println("点击任意键唤醒线程 ..."); sc.nextLine(); });

=== 点击查看top目录 ===

3.1.2 Thread 构造方法
java.lang.Thread#Thread(java.lang.Runnable, java.lang.String) public Thread(Runnable target) { init(null, target, "Thread-" + nextThreadNum(), 0); }

=== 点击查看top目录 ===

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 剖析 (十六)

=== 点击查看top目录 ===

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; // 获取线程数)

=== 点击查看top目录 ===

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 数 )

=== 点击查看top目录 ===

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 打印操作。

=== 点击查看top目录 ===

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); 递归进行复制

=== 点击查看top目录 ===

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

=== 点击查看top目录 ===

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 剖析 (十六)

最新回复(0)