我的原則:先會用再說,內部慢慢來。
學以致用,根據場景學源碼
一、前言
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 剖析 (十六)