多線程系列(十七) -線程組介紹

一、簡介

在之前的多線程系列文章中,我們陸陸續續的介紹了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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章