第6章 ThreadGroup詳細講解(Java高并發編程詳解:多線程與系統設計)

1.ThreadGroup 與 Thread

????????在Java程序中, 默認情況下, 新的線程都會被加入到main線程所在的group中, main線程的group名字同線程名。如同線程存在父子關系一樣, Thread Group同樣也存在父子關系。圖6-1就很好地說明了父子thread、父子thread Group以及thread和group之間的層次關系

2.創建ThreadGroup

創建Thread Group的語法如下:

public Thread Group(String name)
public Thread Group(Thread Group parent, String name)

創建Thread Group的語法非常簡單, 可通過上面某個構造函數來創建, 第一個構造函數為Thread Group賦予了名字, 但是該Thread Group的父Thread Group是創建它的線程所在的Thread Group; 第二個Thread Group的構造函數賦予group名字的同時又顯式地指定了父Group。


public class TestThreadGroup {public static void main(String[] args) {ThreadGroup currentGroup = Thread.currentThread().getThreadGroup();ThreadGroup group1 = new ThreadGroup("group1");System.out.println(group1.getParent() == currentGroup);ThreadGroup group2 = new ThreadGroup(group1, "Group2");System.out.println(group2.getParent() == group1);}
}

3.復制Thread數組和ThreadGroup數組

3,1復制Thread數組

public int enumerate(Thread[] list);
public int enumerate(Thread[] list, boolean recurse);

上述兩個方法, 會將Thread Group中的active線程全部復制到Thread數組中, 其中recurse參數如果為true, 則該方法會將所有子group中的active線程都遞歸到Thread數組中, enumerate(Thread[] list) 實際上等價于enumerate(Thread[] true) , 上面兩個方法都調用了Thread Group的私有方法enumerate:

 private int enumerate(Thread list[], int n, boolean recurse) {int ngroupsSnapshot = 0;ThreadGroup[] groupsSnapshot = null;synchronized (this) {if (destroyed) {return 0;}int nt = nthreads;if (nt > list.length - n) {nt = list.length - n;}for (int i = 0; i < nt; i++) {if (threads[i].isAlive()) {list[n++] = threads[i];}}if (recurse) {ngroupsSnapshot = ngroups;if (groups != null) {groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);} else {groupsSnapshot = null;}}}if (recurse) {for (int i = 0 ; i < ngroupsSnapshot ; i++) {n = groupsSnapshot[i].enumerate(list, n, true);}}return n;}

舉一例:enumerate方法的使用

import java.util.concurrent.TimeUnit;public class TestThreadGroup {public static void main(String[] args) throws InterruptedException {ThreadGroup myGroup = new ThreadGroup("mygroup");Thread th  = new Thread(myGroup,()-> {while(true) {try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}}},"MyThread");th.start();TimeUnit.MILLISECONDS.sleep(2);ThreadGroup mainGroup = Thread.currentThread().getThreadGroup();Thread[] list = new Thread[mainGroup.activeCount()];int recuseSize = mainGroup.enumerate(list);System.out.println(recuseSize);recuseSize = mainGroup.enumerate(list,false);System.out.println(recuseSize);}
}

上面的代碼運行之后, 最后一個輸出會比第一個少1, 那是因為代碼中將遞歸recurse設置為了false, my Group中的線程將不會包含在內。

3.2 復制ThreadGroup數組

public int enumerate(Thread Group[] list);
public int enumerate(Thread Group[] list, boolean recurse);

和復制Thread數組類似, 上述兩個方法, 主要用于復制當前Thread Group的子Group,同樣recurse會決定是否以遞歸的方式復制。

import java.util.concurrent.TimeUnit;public class TestCopyThreadGroup {public static void main(String[] args) throws InterruptedException {ThreadGroup myGroup1 = new ThreadGroup("MyGroup1");ThreadGroup myGroup2 = new ThreadGroup(myGroup1, "MyGroup2");TimeUnit.MILLISECONDS.sleep(2);ThreadGroup mainGroup = Thread.currentThread().getThreadGroup();ThreadGroup[] list = new ThreadGroup[mainGroup.activeCount()];int recurseSize = mainGroup.enumerate(list);System.out.println(recurseSize);recurseSize = mainGroup.enumerate(list,false);System.out.println(recurseSize);}
}

在代碼清單6-3中, my Group 1的父group為main Group, 而my Group 2的父group為my Group 1, 因此上述的代碼運行之后, 遞歸復制的結果為2, 不遞歸的情況下為1。

4.ThreadGroup操作

4.1ThreadGroup的基本操作

import java.util.concurrent.TimeUnit;public class ThreadGroupBasic {public static void main(String[] args) {ThreadGroup group = new ThreadGroup("group1");Thread thread = new Thread(group,() -> {while(true) {try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}}}, "thread");thread.setDaemon(true);thread.start();ThreadGroup mainGroup = Thread.currentThread().getThreadGroup();System.out.println("activeCount=" + mainGroup.activeCount());System.out.println("activeGroupCount=" + mainGroup.activeGroupCount());mainGroup.list();System.out.println("--------------------------");System.out.println("parentOf=" + mainGroup.parentOf(group));}
}
  • activeCount() 用于獲取group中活躍的線程, 這只是個估計值, 并不能百分之百地保證數字一定正確, 原因前面已經分析過, 該方法會遞歸獲取其他子group中的活躍線程。
  • activeGroupCount() 用于獲取group中活躍的子group, 這也是一個近似估值, 該方法也會遞歸獲取所有的子group。
  • getMaxPriority() 用于獲取group的優先級,默認情況下,Group的優先級為10,在該group中, 所有線程的優先級都不能大于group的優先級。
  • getName() 用于獲取group的名字。
  • getParent() 用于獲取group的父group, 如果父group不存在, 則會返回null, 比如system group的父group就為null。
  • list() 該方法沒有返回值, 執行該方法會將group中所有的活躍線程信息全部輸出到控制臺, 也就是System.out。
  • parentOf(Thread Group g) 會判斷當前group是不是給定group的父group, 另外如果給定的group就是自己本身,那么該方法也會返回true。
  • setMaxPriority(int pri) 會指定group的最大優先級, 最大優先級不能超過父group的最大優先級, 執行該方法不僅會改變當前group的最大優先級, 還會改變所有子group的最大優先級

4.2 ThreadGroup的interrupt

interrupt一個thread group會導致該group中所有的active線程都被interrupt, 也就是說該group中每一個線程的interrupt標識都被設置了, 下面是Thread Group interrupt方法的源碼:
?

public final void interrupt() {int ngroupsSnapshot;ThreadGroup[] groupsSnapshot;synchronized (this) {checkAccess();for (int i = 0 ; i < nthreads ; i++) {threads[i].interrupt();}ngroupsSnapshot = ngroups;if (groups != null) {groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);} else {groupsSnapshot = null;}}for (int i = 0 ; i < ngroupsSnapshot ; i++) {groupsSnapshot[i].interrupt();}}

interrupt方法案例:


import java.util.concurrent.TimeUnit;public class TestThreadInterrupt {public static void main(String[] args) throws InterruptedException {ThreadGroup group = new ThreadGroup("TestGroup");new Thread(group,() -> {while(true) {try {TimeUnit.MILLISECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}}},"t1").start();new Thread(group,()-> {try {TimeUnit.MILLISECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}},"t2").start();TimeUnit.MILLISECONDS.sleep(2);group.interrupt();}
}

4.3ThreadGroup的destroy

destroy用于銷毀Thread Group, 該方法只是針對一個沒有任何active線程的group進行一次destroy標記, 調用該方法的直接結果是在父group中將自己移除:

public class ThreadGroupDestroy {public static void main(String[] args) {ThreadGroup group = new ThreadGroup("TestGroup");ThreadGroup mainGroup = Thread.currentThread().getThreadGroup();System.out.println("group.isDestroyed=" + group.isDestroyed());mainGroup.list();group.destroy();System.out.println("group.isDestroyed=" + group.isDestroyed());mainGroup.list();}
}

4.4守護ThreadGroup

線程可以設置為守護線程, Thread Group也可以設置為守護Thread Group, 但是若將一個Thread Group設置為daemon, 也并不會影響線程的daemon屬性, 如果一個Thread Group的daemon被設置為true, 那么在group中沒有任何active線程的時候該group將自動destroy, 下面我們給出一個簡單的例子來對其進行說明:


import java.util.concurrent.TimeUnit;public class ThreadGroupDaemon {public static void main(String[] args) throws InterruptedException {ThreadGroup group = new ThreadGroup("Group1");new Thread(group,()-> {try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}},"group1-thread1").start();ThreadGroup group2 = new ThreadGroup("Group2");new Thread(group2,()-> {try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}},"group2-thread1").start();group2.setDaemon(true);TimeUnit.SECONDS.sleep(3);System.out.println(group.isDestroyed());System.out.println(group2.isDestroyed());}
}

在上面的代碼中, 第二個group的daemon被設置為true, 當其中沒有active線程的時候, 該group將會自動被destroy, 而第一個group則相反。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/66439.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/66439.shtml
英文地址,請注明出處:http://en.pswp.cn/web/66439.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

nginx常用配置 (含負載均衡、反向代理、限流、Gzip壓縮、圖片防盜鏈 等示例)

nginx的配置文件通常在 /etc/nginx/nginx.conf , /etc/nginx/conf.d/*.conf 中&#xff0c; 一般直接 改 conf.d目錄下的 default.conf文件&#xff0c; 然后 先檢測配置文件是否有錯誤 nginx -t 再重新加載配置文件 或 重啟nginx&#xff0c;命令如下 nginx -s reload 或…

Python編程與在線醫療平臺數據挖掘與數據應用交互性研究

一、引言 1.1 研究背景與意義 在互聯網技術飛速發展的當下,在線醫療平臺如雨后春筍般涌現,為人們的就醫方式帶來了重大變革。這些平臺打破了傳統醫療服務在時間和空間上的限制,使患者能夠更加便捷地獲取醫療資源。據相關報告顯示,中國基于互聯網的醫療保健行業已進入新的…

Linux網絡_套接字_UDP網絡_TCP網絡

一.UDP網絡 1.socket()創建套接字 #include<sys/socket.h> int socket(int domain, int type, int protocol);domain (地址族): AF_INET網絡 AF_UNIX本地 AF_INET&#xff1a;IPv4 地址族&#xff0c;適用于 IPv4 協議。用于網絡通信AF_INET6&#xff1a;IPv6 地址族&a…

1 行命令引發的 Go 應用崩潰

一、前言 不久前&#xff0c;阿里云 ARMS 團隊、編譯器團隊、MSE 團隊攜手合作&#xff0c;共同發布并開源了 Go 語言的編譯時自動插樁技術。該技術以其零侵入的特性&#xff0c;為 Go 應用提供了與 Java 監控能力相媲美的解決方案。開發者只需將 go build 替換為新編譯命令 o…

R語言的并發編程

R語言的并發編程 引言 在現代計算中&#xff0c;如何有效地利用計算資源進行數據處理和分析已成為一個重要的研究方向。尤其在大數據時代&#xff0c;數據量的急劇增加讓單線程處理方式顯得力不從心。為了解決這一問題&#xff0c;各種編程語言都開展了并發編程的研究和應用。…

Flink(十):DataStream API (七) 狀態

1. 狀態的定義 在 Apache Flink 中&#xff0c;狀態&#xff08;State&#xff09; 是指在數據流處理過程中需要持久化和追蹤的中間數據&#xff0c;它允許 Flink 在處理事件時保持上下文信息&#xff0c;從而支持復雜的流式計算任務&#xff0c;如聚合、窗口計算、聯接等。狀…

C#項目生成時提示缺少引用

問題描述 剛從git或svn拉取下來的C#項目&#xff0c;在VS生成時提示缺少引用 解決方案 1、從“管理NuGet程序包”中下載并安裝缺少的引用&#xff0c;如果引用較多逐個下載安裝會比較麻煩&#xff0c;建議采用下面第2種方案處理 2、通過命令對所有缺少引用進行安裝 &#…

EAMM: 通過基于音頻的情感感知運動模型實現的一次性情感對話人臉合成

EAMM: 通過基于音頻的情感感知運動模型實現的一次性情感對話人臉合成 1所有的材料都可以在EAMM: One-Shot Emotional Talking Face via Audio-Based Emotion-Aware Motion Model網站上找到。 摘要 盡管音頻驅動的對話人臉生成技術已取得顯著進展&#xff0c;但現有方法要么忽…

BeanFactory 是什么?它與 ApplicationContext 有什么區別?

談到Spring&#xff0c;那勢必要講講容器 BeanFactory 和 ApplicationContext。 BeanFactory是什么&#xff1f; BeanFactory&#xff0c;其實就是 Spring 容器&#xff0c;用于管理和操作 Spring 容器中的 Bean。可能此時又有初學的小伙伴會問&#xff1a;Bean 是什么&#x…

【深度學習】Huber Loss詳解

文章目錄 1. Huber Loss 原理詳解2. Pytorch 代碼詳解3.與 MSELoss、MAELoss 區別及各自優缺點3.1 MSELoss 均方誤差損失3.2 MAELoss 平均絕對誤差損失3.3 Huber Loss 4. 總結4.1 優化平滑4.2 梯度較好4.3 為什么說 MSE 是平滑的 1. Huber Loss 原理詳解 Huber Loss 是一種結合…

python實現pdf轉word和excel

一、引言   在辦公中&#xff0c;我們經常遇收到pdf文件格式&#xff0c;因為pdf格式文件不易修改&#xff0c;當我們需要編輯這些pdf文件時&#xff0c;經常需要開通會員或收費功能才能使用編輯功能。今天&#xff0c;我要和大家分享的&#xff0c;是如何使用python編程實現…

【PyCharm】連接Jupyter Notebook

【PyCharm】相關鏈接 【PyCharm】連接 Git【PyCharm】連接Jupyter Notebook【PyCharm】快捷鍵使用【PyCharm】遠程連接Linux服務器【PyCharm】設置為中文界面 【PyCharm】連接Jupyter Notebook PyCharm連接Jupyter Notebook的過程可以根據不同的需求分為 本地連接 和 遠程連…

Java鎖 公平鎖和非公平鎖 ReentrantLock() 深入源碼解析

賣票問題 我們現在有五個售票員 五個線程分別賣票 賣票 ReentrantLock(); 運行后全是 a 對象獲取 非公平鎖缺點之一 容易出現鎖饑餓 默認是使用的非公平鎖 也可以傳入一個 true 參數 使其變成公平鎖 生活中排隊講求先來后到 視為公平 程序中的公平性也是符合請求鎖的絕對…

「劉一哥GIS」系列專欄《GRASS GIS零基礎入門實驗教程(配套案例數據)》專欄上線了

「劉一哥GIS」系列專欄《GRASS GIS零基礎入門實驗教程》全新上線了&#xff0c;歡迎廣大GISer朋友關注&#xff0c;一起探索GIS奧秘&#xff0c;分享GIS價值&#xff01; 本專欄以實戰案例的形式&#xff0c;深入淺出地介紹了GRASS GIS的基本使用方法&#xff0c;用一個個實例講…

企業級NoSQL數據庫Redis

1.瀏覽器緩存過期機制 1.1 最后修改時間 last-modified 瀏覽器緩存機制是優化網頁加載速度和減少服務器負載的重要手段。以下是關于瀏覽器緩存過期機制、Last-Modified 和 ETag 的詳細講解&#xff1a; 一、Last-Modified 頭部 定義&#xff1a;Last-Modified 表示服務器上資源…

使用Flask和Pydantic實現參數驗證

使用Flask和Pydantic實現參數驗證 1 簡介 Pydantic是一個用于數據驗證和解析的 Python 庫&#xff0c;版本2的性能有較大提升&#xff0c;很多框架使用Pydantic做數據校驗。 # 官方參考文檔 https://docs.pydantic.dev/latest/# Github地址 https://github.com/pydantic/pyd…

ScratchLLMStepByStep:訓練自己的Tokenizer

1. 引言 分詞器是每個大語言模型必不可少的組件&#xff0c;但每個大語言模型的分詞器幾乎都不相同。如果要訓練自己的分詞器&#xff0c;可以使用huggingface的tokenizers框架&#xff0c;tokenizers包含以下主要組件&#xff1a; Tokenizer: 分詞器的核心組件&#xff0c;定…

C# OpenCvSharp 部署3D人臉重建3DDFA-V3

目錄 說明 效果 模型信息 landmark.onnx net_recon.onnx net_recon_mbnet.onnx retinaface_resnet50.onnx 項目 代碼 下載 參考 C# OpenCvSharp 部署3D人臉重建3DDFA-V3 說明 地址&#xff1a;https://github.com/wang-zidu/3DDFA-V3 3DDFA_V3 uses the geometri…

從零開始學數據庫 day2 DML

從零開始學數據庫&#xff1a;DML操作詳解 在今天的數字化時代&#xff0c;數據庫的使用已經成為了各行各業的必備技能。無論你是想開發一個簡單的應用&#xff0c;還是想要管理復雜的數據&#xff0c;掌握數據庫的基本操作都是至關重要的。在這篇博客中&#xff0c;我們將專注…

Java 8 Stream API

文章目錄 Java 8 Stream API1. Stream2. Stream 的創建3. 常見的 Stream 操作3.1 中間操作3.2 終止操作 4. Stream 的并行操作 Java 8 Stream API Java 8 引入了 Stream API&#xff0c;使得對集合類&#xff08;如 List、Set 等&#xff09;的操作變得更加簡潔和直觀。Stream…