什么是線程組?你學(xué)會了嗎?
一、簡介
在之前的多線程系列文章中,我們陸陸續(xù)續(xù)的介紹了Thread線程類相關(guān)的知識和用法,其實在Thread類上還有一層ThreadGroup類,也就是線程組。
今天我們就一起來簡單的聊聊線程組相關(guān)的知識和用法。
二、什么是線程組
線程組,簡單來說就是多個線程的集合,它的出現(xiàn)主要是為了更方便的管理線程。
從結(jié)構(gòu)角度看,線程組與線程之間其實是一個父子結(jié)構(gòu),一個線程組可以擁有幾個線程,同時也可以擁有幾個線程組。整個組織結(jié)構(gòu)像一棵樹一樣,每個線程一定有一個線程組,線程組可能又有一個父線程組,追溯到根節(jié)點(diǎn)就是一個系統(tǒng)線程組。
線程組與線程之間的關(guān)系,可以用如下圖來描述。
圖片
比如,我們通常創(chuàng)建的main方法,對應(yīng)的是main線程,它所屬的是main線程組,main線程組的父級是是system系統(tǒng)線程組。
public static void main(String[] args) {
Thread currentThread = Thread.currentThread();
ThreadGroup currentThreadGroup = currentThread.getThreadGroup();
ThreadGroup systemThreadGroup = currentThreadGroup.getParent();
System.out.println("currentThread:" + currentThread.getName());
System.out.println("currentThreadGroup:" + currentThreadGroup.getName());
System.out.println("systemThreadGroup:" + systemThreadGroup.getName());
}
輸出結(jié)果如下:
currentThread:main
currentThreadGroup:main
systemThreadGroup:system
其中system線程組就是根節(jié)點(diǎn),再上一層就沒有了,如果調(diào)用會拋空指針異常。
線程組最主要的作用是:可以實現(xiàn)批量管理線程或者線程組,有效的對線程或者線程組對象進(jìn)行檢查、嘗試中斷等操作。
下面我們就一起來看看ThreadGroup的常用方法和使用技巧。
三、線程組用法詳解
3.1、構(gòu)造方法介紹
ThreadGroup提供了兩個構(gòu)造方法,內(nèi)容如下:
方法 | 描述 |
ThreadGroup(String name) | 根據(jù)線程組名稱創(chuàng)建線程組,其父線程組為main線程組 |
ThreadGroup(ThreadGroup parent, String name) | 根據(jù)線程組名稱創(chuàng)建線程組,其父線程組為指定的 parent 線程組 |
其中支持指定父級線程組的方法,在實際的使用中比較常見。
下面,我們演示一下這兩個構(gòu)造函數(shù)的用法:
public static void main(String[] args) {
ThreadGroup subThreadGroup1 = new ThreadGroup("sub1");
ThreadGroup subThreadGroup2 = new ThreadGroup(subThreadGroup1, "sub2");
System.out.println("sub1 parent thread group name:" + subThreadGroup1.getParent().getName());
System.out.println("sub2 parent thread group name:" + subThreadGroup2.getParent().getName());
}
輸出結(jié)果如下:
sub1 parent thread group name:main
sub2 parent thread group name:sub1
3.2、核心方法介紹
ThreadGroup提供了很多有用的方法,下面整理了一些方法的簡要介紹,內(nèi)容如下:
方法 | 描述 |
public final String getName() | 返回此線程組的名稱 |
public final ThreadGroup getParent() | 返回此線程組的父級 |
public final boolean parentOf(ThreadGroup g) | 測試此線程組是線程組參數(shù)還是其父級線程組之一 |
public int activeCount() | 返回此線程組及其子組中活動線程的數(shù)量的估計值,遞歸遍歷該線程組中所有的子組,此方法主要用于調(diào)試和監(jiān)視目的 |
public int activeGroupCount () | 返回此線程組及其子組中活動組的數(shù)目的估計值。遞歸遍歷該線程組中的所有子群,此方法主要用于調(diào)試和監(jiān)視目的 |
public final void checkAccess() | 確定當(dāng)前運(yùn)行的線程是否具有修改此線程組的權(quán)限 |
public int enumerate(Thread[] list) | 將這個線程組復(fù)制到它所在的組及其子組中 |
public final void destroy() | 銷毀此線程組及其所有子組,當(dāng)線程組還要子線程或者子線程組,會拋異常 |
public boolean isDestroyed() | 測試此線程組是否已被銷毀 |
public final int getMaxPriority() | 返回此線程組的最大優(yōu)先級 |
public final void setMaxPriority(int pri) | 設(shè)置組的最大優(yōu)先級。線程組中具有較高優(yōu)先級的線程不會受到影響 |
public final boolean isDaemon() | 測試此線程組是否是守護(hù)線程組 |
public final void setDaemon(boolean daemon) | 修改此線程組的守護(hù)進(jìn)程狀態(tài) |
public final void interrupt() | 嘗試中斷此線程組中的所有線程 |
public void list() | 將此線程組的信息打印到標(biāo)準(zhǔn)輸出。此方法僅用于調(diào)試 |
下面我們抽取幾個比較常見的方法,進(jìn)行演示介紹。
3.2.1、activeCount 方法
activeCount()方法用于返回此線程組及其子組中活動線程的數(shù)量的估計值,因為線程的數(shù)量是動態(tài)發(fā)生變化的,返回的值只是一個估計值。
我們看一個簡單的例子就知道了。
public class MyThread extends Thread{
public MyThread(ThreadGroup group, String name) {
super(group, name);
}
@Override
public void run() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class MyThreadMainTest {
public static void main(String[] args) throws Exception {
ThreadGroup tg = new ThreadGroup("group1");
MyThread t1 = new MyThread (tg, "t1");
MyThread t2 = new MyThread (tg, "t2");
t1.start();
t2.start();
System.out.println("線程組的名稱:" + tg.getName() + ",活動的線程數(shù):" + tg.activeCount());
Thread.sleep(1000);
System.out.println("線程組的名稱:" + tg.getName() + ",活動的線程數(shù):" + tg.activeCount());
}
}
輸出結(jié)果如下:
線程組的名稱:group1,活動的線程數(shù):2
線程組的名稱:group1,活動的線程數(shù):0
第一次檢查線程都處于運(yùn)行狀態(tài),因此活動的線程數(shù)為 2;過 1 秒之后,線程運(yùn)行結(jié)束,活動的線程數(shù)為 0。
3.2.2、isDaemon 方法
setDaemon()方法用于測試此線程組是否是守護(hù)線程組。
需要注意的是:后臺線程組和后臺線程是兩個概念,后臺線程組的特性是最后一個線程執(zhí)行完或最后一個線程被銷毀時,后臺線程組自動銷毀,線程組只是為了統(tǒng)一管理線程的一個方式,跟后臺線程有區(qū)別!
例子如下:
public class MyThread extends Thread{
public MyThread(ThreadGroup group, String name) {
super(group, name);
}
@Override
public void run() {
System.out.println("當(dāng)前線程:" + Thread.currentThread().getName() + ",是否后臺線程:" + Thread.currentThread().isDaemon());
System.out.println("當(dāng)前線程組:" + Thread.currentThread().getThreadGroup().getName() + ",是否后臺線程組:" + Thread.currentThread().getThreadGroup().isDaemon());
}
}
public class MyThreadMainTest4 {
public static void main(String[] args) throws Exception {
ThreadGroup mainGroup = Thread.currentThread().getThreadGroup();
new MyThread(mainGroup, "t1").start();
Thread.sleep(100);
// 設(shè)置守護(hù)線程組
ThreadGroup tg = new ThreadGroup("group1");
tg.setDaemon(true);
new MyThread(tg,"t2").start();
}
}
輸出結(jié)果如下:
當(dāng)前線程:t1,是否后臺線程:false
當(dāng)前線程組:main,是否后臺線程組:false
當(dāng)前線程:t2,是否后臺線程:false
當(dāng)前線程組:group1,是否后臺線程組:true
3.2.3、interrupt 方法
interrupt()方法用于嘗試中斷此線程組中的所有線程。如果正在運(yùn)行的線程沒有進(jìn)入阻塞,是無法中斷的。
例子如下:
public class MyThreadA extends Thread{
public MyThreadA(ThreadGroup group, String name) {
super(group, name);
}
@Override
public void run() {
System.out.println("線程:" + Thread.currentThread().getName() + ",開始運(yùn)行");
String t;
for (int i = 0; i < 1000000000; i++) {
t = i + "";
}
System.out.println("線程:" + Thread.currentThread().getName() + ",停止運(yùn)行");
}
}
public class MyThreadB extends Thread{
public MyThreadB(ThreadGroup group, String name) {
super(group, name);
}
@Override
public void run() {
System.out.println("線程:" + Thread.currentThread().getName() + ",開始運(yùn)行");
while (!Thread.interrupted()){
}
System.out.println("線程:" + Thread.currentThread().getName() + ",停止運(yùn)行");
}
}
public class MyThreadC extends Thread{
public MyThreadC(ThreadGroup group, String name) {
super(group, name);
}
@Override
public void run() {
System.out.println("線程:" + Thread.currentThread().getName() + ",開始運(yùn)行");
try {
Thread.sleep(1000);
} catch (Exception e){
// e.printStackTrace();
}
System.out.println("線程:" + Thread.currentThread().getName() + ",停止運(yùn)行");
}
}
public class MyThreadMainTest {
public static void main(String[] args) throws Exception {
ThreadGroup tg = new ThreadGroup("group1");
new MyThreadA(tg,"t1").start();
new MyThreadB(tg,"t2").start();
new MyThreadC(tg,"t3").start();
// 嘗試中斷線程組里面的線程
tg.interrupt();
}
}
輸出結(jié)果如下:
線程:t1,開始運(yùn)行
線程:t2,開始運(yùn)行
線程:t2,停止運(yùn)行
線程:t3,開始運(yùn)行
線程:t3,停止運(yùn)行
線程t1只有等它運(yùn)行結(jié)束,通過interrupt()不能中斷程序!
四、小結(jié)
本文主要圍繞線程組的一些基本概念以及常用方法,并結(jié)合了一些簡單示例進(jìn)行介紹。
線程組的出現(xiàn)更多的是便于有組織的管理線程,比如 Java 的線程池就用到了線程組,更多的線程知識,我們在后續(xù)的文章中會進(jìn)行介紹。
如果有描述不對的地方,歡迎網(wǎng)友留言指出。
五、參考
1、https://www.cnblogs.com/xrq730/p/4856072.html
2、https://cloud.tencent.com/developer/article/1633465