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

来源:https://www.cnblogs.com/dxflqm/p/18068251
-Advertisement-
Play Games

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


一、簡介

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


作者:程式員志哥
出處:pzblog.cn
資源:微信搜【程式員志哥】關註我,回覆 【技術資料】有我準備的一線程式必備電腦書籍、大廠面試資料和免費電子書。 希望可以幫助大家提升技術和能力。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 零售商家為什麼要建設線上商城 傳統的實體門店服務範圍有限,只能吸引周邊500米內的消費者。因此,如何拓展服務範圍,吸引更多消費者到店,成為了店家迫切需要解決的問題。 缺乏忠實顧客,客戶基礎不穩,往往是一次性購物,門店無法形成有效的顧客迴流。在當前的市場環境下,構建並維護粉絲群體,成為了商家的核心競爭 ...
  • 背景 在一個微服務架構的項目中,一個業務操作可能涉及到多個服務,這些服務往往是獨立部署,構成一個個獨立的系統。這種分散式的系統架構往往面臨著分散式事務的問題。為了保證系統數據的一致性,我們需要確保這些服務中的操作要麼全部成功,要麼全部失敗。通過使用RocketMQ實現分散式事務,我們可以協調這些服務 ...
  • 最近項目中有一個需要使用QT生成固定長度隨機字元串的需求,需求也很簡單,就是生成一個n位的僅包含0-9以及大寫字母的字元串,因為這也是第一次使用QT自身的隨機數,這裡就做一下簡單記錄。 廢話不多說,直接上代碼。 1 QString getRandomString(int length) 2 { 3 ...
  • 8.1 C++內聯函數 提出的目的:為了提高程式運行速度。 內聯函數和普通函數的區別: 編譯方式: 內聯函數在編譯時會被直接替換到調用處,而不是像普通函數那樣通過函數調用的方式執行。這樣可以減少函數調用的開銷,提高程式執行效率。 普通函數則是通過函數調用的方式執行,會涉及函數棧的壓棧和出棧操作。 代 ...
  • Java Iterator Iterator 介面提供了一種迭代集合的方法,即順序訪問集合中的每個元素。它支持 hasNext() 和 next() 方法,用於檢查是否存在下一個元素以及獲取下一個元素。 獲取 Iterator 可以使用集合的 iterator() 方法獲取 Iterator 實例: ...
  • 摘要: 銀行卡歸屬地查詢介面是一種高效的方式,通過銀行卡號查詢銀行名稱、卡種、卡品牌以及發卡省份和城市等信息。本文將詳細介紹如何使用該介面,並附帶代碼說明。同時,也介紹了介面的特點和適用範圍,讓讀者能夠充分瞭解和運用該介面,方便快捷地獲取銀行卡發卡行所在地信息。 一、介面簡介 銀行卡歸屬地查詢介面是 ...
  • 1. 本篇文章目標 將下麵的excel中的寄存器表單讀入並構建一個字典 2. openpyxl的各種基本使用方法 2.1 打開工作簿 wb = openpyxl.load_workbook('test_workbook.xlsx') 2.2 獲取工作簿中工作表名字並得到工作表 ws = wb[wb. ...
  • 拓展閱讀 linux Shell 命令行-00-intro 入門介紹 linux Shell 命令行-02-var 變數 linux Shell 命令行-03-array 數組 linux Shell 命令行-04-operator 操作符 linux Shell 命令行-05-test 驗證是否符 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...