多執行緒系列(十七) -執行緒組介紹

程序员志哥發表於2024-03-12

一、簡介

在之前的多執行緒系列文章中,我們陸陸續續的介紹了Thread執行緒類相關的知識和用法,其實在Thread類上還有一層ThreadGroup類,也就是執行緒組。

今天我們就一起來簡單的聊聊執行緒組相關的知識和用法。

二、什麼是執行緒組

執行緒組,簡單來說就是多個執行緒的集合,它的出現主要是為了更方便的管理執行緒。

從結構角度看,執行緒組與執行緒之間其實是一個父子結構,一個執行緒組可以擁有幾個執行緒,同時也可以擁有幾個執行緒組。整個組織結構像一棵樹一樣,每個執行緒一定有一個執行緒組,執行緒組可能又有一個父執行緒組,追溯到根節點就是一個系統執行緒組。

執行緒組與執行緒之間的關係,可以用如下圖來描述。

比如,我們通常建立的main方法,對應的是main執行緒,它所屬的是main執行緒組,main執行緒組的父級是是system系統執行緒組。

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());
}

輸出結果如下:

currentThread:main
currentThreadGroup:main
systemThreadGroup:system

其中system執行緒組就是根節點,再上一層就沒有了,如果呼叫會拋空指標異常。

執行緒組最主要的作用是:可以實現批次管理執行緒或者執行緒組,有效的對執行緒或者執行緒組物件進行檢查、嘗試中斷等操作。

下面我們就一起來看看ThreadGroup的常用方法和使用技巧。

三、執行緒組用法詳解

3.1、構造方法介紹

ThreadGroup提供了兩個構造方法,內容如下:

方法 描述
ThreadGroup(String name) 根據執行緒組名稱建立執行緒組,其父執行緒組為main執行緒組
ThreadGroup(ThreadGroup parent, String name) 根據執行緒組名稱建立執行緒組,其父執行緒組為指定的 parent 執行緒組

其中支援指定父級執行緒組的方法,在實際的使用中比較常見。

下面,我們演示一下這兩個建構函式的用法:

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());
}

輸出結果如下:

sub1 parent thread group name:main
sub2 parent thread group name:sub1

3.2、核心方法介紹

ThreadGroup提供了很多有用的方法,下面整理了一些方法的簡要介紹,內容如下:

方法 描述
public final String getName() 返回此執行緒組的名稱
public final ThreadGroup getParent() 返回此執行緒組的父級
public final boolean parentOf(ThreadGroup g) 測試此執行緒組是執行緒組引數還是其父級執行緒組之一
public int activeCount() 返回此執行緒組及其子組中活動執行緒的數量的估計值,遞迴遍歷該執行緒組中所有的子組,此方法主要用於除錯和監視目的
public int activeGroupCount () 返回此執行緒組及其子組中活動組的數目的估計值。遞迴遍歷該執行緒組中的所有子群,此方法主要用於除錯和監視目的
public final void checkAccess() 確定當前執行的執行緒是否具有修改此執行緒組的許可權
public int enumerate(Thread[] list) 將這個執行緒組複製到它所在的組及其子組中
public final void destroy() 銷燬此執行緒組及其所有子組,當執行緒組還要子執行緒或者子執行緒組,會拋異常
public boolean isDestroyed() 測試此執行緒組是否已被銷燬
public final int getMaxPriority() 返回此執行緒組的最大優先順序
public final void setMaxPriority(int pri) 設定組的最大優先順序。執行緒組中具有較高優先順序的執行緒不會受到影響
public final boolean isDaemon() 測試此執行緒組是否是守護執行緒組
public final void setDaemon(boolean daemon) 修改此執行緒組的守護程序狀態
public final void interrupt() 嘗試中斷此執行緒組中的所有執行緒
public void list() 將此執行緒組的資訊列印到標準輸出。此方法僅用於除錯

下面我們抽取幾個比較常見的方法,進行演示介紹。

3.2.1、activeCount 方法

activeCount()方法用於返回此執行緒組及其子組中活動執行緒的數量的估計值,因為執行緒的數量是動態發生變化的,返回的值只是一個估計值。

我們看一個簡單的例子就知道了。

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() + ",活動的執行緒數:" +  tg.activeCount());
        Thread.sleep(1000);
        System.out.println("執行緒組的名稱:" +  tg.getName() + ",活動的執行緒數:" +  tg.activeCount());
    }
}

輸出結果如下:

執行緒組的名稱:group1,活動的執行緒數:2
執行緒組的名稱:group1,活動的執行緒數:0

第一次檢查執行緒都處於執行狀態,因此活動的執行緒數為 2;過 1 秒之後,執行緒執行結束,活動的執行緒數為 0。

3.2.2、isDaemon 方法

setDaemon()方法用於測試此執行緒組是否是守護執行緒組。

需要注意的是:後臺執行緒組和後臺執行緒是兩個概念,後臺執行緒組的特性是最後一個執行緒執行完或最後一個執行緒被銷燬時,後臺執行緒組自動銷燬,執行緒組只是為了統一管理執行緒的一個方式,跟後臺執行緒有區別!

例子如下:

public class MyThread extends Thread{

    public MyThread(ThreadGroup group, String name) {
        super(group, name);
    }

    @Override
    public void run() {
        System.out.println("當前執行緒:" + Thread.currentThread().getName() + ",是否後臺執行緒:" +  Thread.currentThread().isDaemon());
        System.out.println("當前執行緒組:" + 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);

        // 設定守護執行緒組
        ThreadGroup tg = new ThreadGroup("group1");
        tg.setDaemon(true);
        new MyThread(tg,"t2").start();
    }
}

輸出結果如下:

當前執行緒:t1,是否後臺執行緒:false
當前執行緒組:main,是否後臺執行緒組:false
當前執行緒:t2,是否後臺執行緒:false
當前執行緒組:group1,是否後臺執行緒組:true
3.2.3、interrupt 方法

interrupt()方法用於嘗試中斷此執行緒組中的所有執行緒。如果正在執行的執行緒沒有進入阻塞,是無法中斷的。

例子如下:

public class MyThreadA extends Thread{

    public MyThreadA(ThreadGroup group, String name) {
        super(group, name);
    }

    @Override
    public void run() {
        System.out.println("執行緒:" + Thread.currentThread().getName() + ",開始執行");
        String t;
        for (int i = 0; i < 1000000000; i++) {
            t = i + "";
        }
        System.out.println("執行緒:" + Thread.currentThread().getName() + ",停止執行");
    }
}
public class MyThreadB extends Thread{

    public MyThreadB(ThreadGroup group, String name) {
        super(group, name);
    }

    @Override
    public void run() {
        System.out.println("執行緒:" + Thread.currentThread().getName() + ",開始執行");
        while (!Thread.interrupted()){
        }
        System.out.println("執行緒:" + Thread.currentThread().getName() + ",停止執行");
    }
}
public class MyThreadC extends Thread{

    public MyThreadC(ThreadGroup group, String name) {
        super(group, name);
    }

    @Override
    public void run() {
        System.out.println("執行緒:" + Thread.currentThread().getName() + ",開始執行");
        try {
            Thread.sleep(1000);
        } catch (Exception e){
//            e.printStackTrace();
        }
        System.out.println("執行緒:" + Thread.currentThread().getName() + ",停止執行");
    }
}
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();
    }
}

輸出結果如下:

執行緒:t1,開始執行
執行緒:t2,開始執行
執行緒:t2,停止執行
執行緒:t3,開始執行
執行緒:t3,停止執行

執行緒t1只有等它執行結束,透過interrupt()不能中斷程式!

四、小結

本文主要圍繞執行緒組的一些基本概念以及常用方法,並結合了一些簡單示例進行介紹。

執行緒組的出現更多的是便於有組織的管理執行緒,比如 Java 的執行緒池就用到了執行緒組,更多的執行緒知識,我們在後續的文章中會進行介紹。

如果有描述不對的地方,歡迎網友留言指出。

五、參考

1、https://www.cnblogs.com/xrq730/p/4856072.html

2、https://cloud.tencent.com/developer/article/1633465

相關文章