Java并發(fā)編程:什么是線程組?它有什么作用?
線程組簡介
在 Java 中,ThreadGroup用于表示一個線程組。我們可以使用ThreadGroup來批量控制線程,更方便地管理線程。
ThreadGroup和Thread之間的關(guān)系非常簡單,就像它們的字面意思一樣:每個Thread必然存在于一個ThreadGroup中,一個Thread不能獨立于ThreadGroup存在。
執(zhí)行main()方法的線程名字是main,如果你在執(zhí)行new Thread()時沒有顯式指定一個ThreadGroup,那么默認(rèn)會將父線程(當(dāng)前正在執(zhí)行new Thread()的線程)的ThreadGroup設(shè)置為子線程的ThreadGroup。
示例代碼:
public class Demo {
public static void main(String[] args) {
Thread subThread = new Thread(() -> {
System.out.println("子線程所在的線程組名稱是:" +
Thread.currentThread().getThreadGroup().getName());
System.out.println("當(dāng)前線程(子線程)的名稱是:" +
Thread.currentThread().getName());
});
subThread.start();
System.out.println("執(zhí)行 main() 方法的線程所在的線程組名稱是:"
+ Thread.currentThread().getThreadGroup().getName());
System.out.println("當(dāng)前線程的名稱是:"
+ Thread.currentThread().getName());
}
}
輸出:
執(zhí)行main()方法的線程所在的線程組名稱是: main
當(dāng)前線程的名稱是: main
子線程所在的線程組名稱是: main
當(dāng)前線程(子線程)的名稱是: Thread-0
線程組是父子結(jié)構(gòu)的,一個線程組可以包含其他線程組,也可以有其他子線程組。從結(jié)構(gòu)上看,線程組是一個樹形結(jié)構(gòu),每個線程屬于一個線程組,而該線程組又有一個父線程組,依此類推,最終可以追溯到根線程組,即System線程組。
結(jié)構(gòu)如下所示:
圖片
- JVM 創(chuàng)建的system線程組是一組用于處理 JVM 系統(tǒng)任務(wù)的線程,比如對象銷毀、垃圾回收(GC)等。
- system線程組的直接子線程組是main線程組,它至少包含一個執(zhí)行main方法的main線程。
- main線程組的子線程組是由應(yīng)用程序創(chuàng)建的線程組。
你可以在main方法中看到 JVM 創(chuàng)建的system線程組和main線程組:
public static void main(String[] args) {
ThreadGroup mainThreadGroup = Thread.currentThread().getThreadGroup();
ThreadGroup systemThreadGroup = mainThreadGroup.getParent();
System.out.println("當(dāng)前線程所在的線程組的父線程組名稱 = " + systemThreadGroup.getName());
System.out.println("當(dāng)前線程所在的線程組名稱 = " + mainThreadGroup.getName());
}
輸出:
當(dāng)前線程所在的線程組的父線程組名稱 = system
當(dāng)前線程所在的線程組名稱 = main
一個線程可以訪問它所屬線程組的信息,但不能訪問它所屬線程組的父線程組或其他線程組的信息。
線程組的結(jié)構(gòu)
首先,我們來看一下ThreadGroup源碼中的成員變量。
public class ThreadGroup implements Thread.UncaughtExceptionHandler {
private final ThreadGroup parent; // 父線程組
String name;
int maxPriority;
boolean destroyed;
boolean daemon;
boolean vmAllowSuspension;
int nUnstartedThreads = 0;
int nthreads; // 子線程數(shù)量
Thread threads[]; // 子線程數(shù)組
int ngroups; // 子線程組數(shù)量
ThreadGroup groups[]; // 子線程組數(shù)組
}
接下來,我們看一下java.lang.ThreadGroup提供的兩個構(gòu)造函數(shù),我添加了一些注釋以便理解。
// 當(dāng) JVM 啟動時,調(diào)用此構(gòu)造函數(shù)創(chuàng)建根線程組。
private ThreadGroup() {
this.name = "system";
this.maxPriority = Thread.MAX_PRIORITY;
this.parent = null;
}
// 默認(rèn)情況下,傳入當(dāng)前 ThreadGroup 作為父 ThreadGroup。新線程組的父線程組是當(dāng)前運行線程的線程組。
public ThreadGroup(String name) {
this(Thread.currentThread().getThreadGroup(), name);
}
// 傳入名稱創(chuàng)建線程組,父線程組由客戶端指定。
public ThreadGroup(ThreadGroup parent, String name) {
this(checkParentAccess(parent), parent, name);
}
// 主要的私有構(gòu)造函數(shù),大多數(shù)參數(shù)從父線程組繼承
private ThreadGroup(Void unused, ThreadGroup parent, String name) {
this.name = name;
this.maxPriority = parent.maxPriority;
this.daemon = parent.daemon;
this.vmAllowSuspension = parent.vmAllowSuspension;
this.parent = parent;
parent.add(this);
}
checkParentAccess()方法用于判斷當(dāng)前運行的線程是否有權(quán)限修改線程組。
以下代碼演示了這兩個構(gòu)造函數(shù)的用法:
public class ConstructDemo {
public static void main(String[] args) {
ThreadGroup subThreadGroup1 = new ThreadGroup("subThreadGroup1");
ThreadGroup subThreadGroup2 = new ThreadGroup(subThreadGroup1, "subThreadGroup2");
System.out.println("subThreadGroup1 的父線程組名稱是:" +
subThreadGroup1.getParent().getName());
System.out.println("subThreadGroup2 的父線程組名稱是:" +
subThreadGroup2.getParent().getName());
}
}
輸出:
subThreadGroup1的父線程組名稱是: main
subThreadGroup2的父線程組名稱是: subThreadGroup1
ThreadGroup 包含的方法
ThreadGroup提供了許多有用的方法,下面簡要介紹其中一些。
方法 | 描述 |
void checkAccess() | 判斷當(dāng)前運行的線程是否有權(quán)限修改線程組。 |
int activeCount() | 返回線程組及其子組中活動線程的估計數(shù)量。 |
int activeGroupCount() | 返回線程組及其子組中活動線程組的估計數(shù)量。 |
void destroy() | 銷毀線程組及其所有子組。 |
int enumerate(Thread[] list) | 將線程組及其子組中的所有活動線程復(fù)制到指定的數(shù)組中。 |
int getMaxPriority() | 返回線程組的最大優(yōu)先級。 |
String getName() | 返回線程組的名稱。 |
ThreadGroup getParent() | 返回線程組的父線程組。 |
void interrupt() | 中斷線程組中的所有線程。 |
boolean isDaemon() | 判斷線程組是否是守護(hù)線程組。 |
void setDaemon(boolean daemon) | 設(shè)置線程組的守護(hù)狀態(tài)。 |
boolean isDestroyed() | 判斷線程組是否已被銷毀。 |
void list() | 將線程組的信息打印到標(biāo)準(zhǔn)輸出。 |
boolean parentOf(ThreadGroup g) | 判斷線程組是否是參數(shù)線程組或其祖先線程組。 |
void suspend() | 掛起線程組中的所有線程。 |
void resume() | 恢復(fù)線程組中所有被掛起的線程。 |
void setMaxPriority(int prt) | 設(shè)置線程組的最大優(yōu)先級。 |
void stop() | 停止線程組中的所有線程。 |
String toString() | 返回線程組的字符串表示。 |
我們選擇其中一些方法來演示用法。
public class ThreadGroupMethodCase {
public static void main(String[] args) throws InterruptedException {
ThreadGroup subgroup1 = new ThreadGroup("subgroup1");
Thread t1 = new Thread(subgroup1, "t1 in subgroup1");
Thread t2 = new Thread(subgroup1, "t2 in subgroup1");
Thread t3 = new Thread(subgroup1, "t3 in subgroup1");
t1.start();
Thread.sleep(50);
t2.start();
int activeThreadCount = subgroup1.activeCount();
System.out.println("線程組 " + subgroup1.getName() + " 中的活動線程數(shù)量:" + activeThreadCount);
ThreadGroup subgroup2 = new ThreadGroup("subgroup2");
Thread t4 = new Thread(subgroup2, "t4 in subgroup2");
ThreadGroup currentThreadGroup = Thread.currentThread().getThreadGroup();
int activeGroupCount = currentThreadGroup.activeGroupCount();
System.out.println("線程組 " + currentThreadGroup.getName() + " 中的活動線程組數(shù)量:" + activeGroupCount);
System.out.println("將當(dāng)前線程組的信息打印到標(biāo)準(zhǔn)輸出:");
currentThreadGroup.list();
}
}
輸出:
線程組 subgroup1 中的活動線程數(shù)量: 2
線程組 main 中的活動線程組數(shù)量: 2
將當(dāng)前線程組的信息打印到標(biāo)準(zhǔn)輸出:
java.lang.ThreadGroup[name=main,maxpri=10]
Thread[main,5,main]
java.lang.ThreadGroup[name=subgroup1,maxpri=10]
java.lang.ThreadGroup[name=subgroup2,maxpri=10]
這里有一個有趣的地方:當(dāng)輸出當(dāng)前線程組中的活動線程數(shù)量時,實際上并沒有計算狀態(tài)為NEW和TERMINATED的線程。所以當(dāng)輸出subgroup1.activeCount()時,實際上只有一個活動線程,即t2,因為t1已經(jīng)結(jié)束,而t3還沒有啟動。
總結(jié)
簡單來說,線程組是一個樹形結(jié)構(gòu),每個線程組下可以有多個線程或多個線程組。線程組可以用于統(tǒng)一控制線程的優(yōu)先級、檢查線程的權(quán)限等。