JAVA篇:Java 多執行緒 (四) 執行緒組ThreadGroup
4、執行緒組ThreadGroup
4.1 什麼是執行緒組
執行緒組的作用是:可以批量管理執行緒或執行緒組物件,有效地對執行緒或執行緒組物件進行組織。
或許需要區分一下執行緒陣列、執行緒池、執行緒組ThreadGroup。
執行緒陣列就是將執行緒放入陣列中,方便做一些簡單的操作(遍歷查詢、執行、join阻塞)。
執行緒池的概念是在python執行緒接觸的,由於python的執行緒提供了通用執行緒呼叫方法的方式來替代執行緒繼承建立,可以維持一個一定數量的執行緒陣列以供使用,減少執行緒頻繁建立銷燬的開銷,原理類似於資料庫連線池。這個用法Java好像並不常用。
還有就是執行緒組ThreadGroup,它所維持的執行緒結構更像是一個樹,提供了一些管理執行緒組的方法。
4.2 ThreadGroup的構造方法
ThreadGroup和Thread一樣定義在java.lang下面,提供了兩個建構函式,其中一個建構函式可指定父執行緒組。
//構造方法1:構造新執行緒組,新執行緒組的預設父項是正在執行執行緒所在的執行緒組 ThreadGroup(String name) //構造方法2:構造新執行緒組,並將指定執行緒組作為父執行緒組 ThreadGroup(ThreadGroup parent, String name) public static void test1(){ ThreadGroup threadGroup1 = new ThreadGroup("執行緒組1"); System.out.println(threadGroup1.getName());//執行緒組1 System.out.println(threadGroup1.getParent().getName());//main }
ThreadGroup提供了方法checkAccess(),用來確定當前執行的執行緒是否有許可權修改此執行緒組。當用戶在使用構建方法在預設或指定執行緒組下構建新的執行緒組,會呼叫該方法,檢查當前執行緒是否有許可權修改父執行緒組,若沒有許可權,會丟擲錯誤“SecurityException”。
4.3 ThreadGroup提供的一些方法
ThreadGroup的方法與Thread向對應,只是提供了一些統一的管理操作的方法。
-
String getName():返回此執行緒組的名稱。
-
ThreadGroup getParent():返回此執行緒組的父級。
-
boolean parentOf(ThreadGroup g):測試此執行緒組是否是其祖先執行緒組之一。
-
void interrupt():中斷此執行緒組中的所有執行緒。
-
void setMaxPriority(int pri):設定組的最大優先順序。
-
int getMaxPriority():返回此執行緒組的最大優先順序。
-
void setDaemon(boolean daemon):更改此執行緒組的守護程式狀態。守護執行緒組最後一個執行緒停止或最後一個執行緒組被銷燬時自動銷燬。
-
boolean isDaemon():測試此執行緒組是否是守護執行緒組。
-
void destroy():銷燬此執行緒組及其所有子組, 此執行緒組必須為空,表示此執行緒組中的所有執行緒已停止。如果執行緒組不為空或執行緒組已被破壞,則丟擲"IllegalThreadStateException"。
-
boolean isDestroyed():測試此執行緒組是否已被破壞。
-
int activeCount():返回此執行緒組及其子組中活動執行緒數的估計。
-
int activeGroupCount():返回此執行緒組及其子組中活動組數的估計。
-
void checkAccess():確定當前執行的執行緒是否有許可權修改此執行緒組。在ThreadGroup中涉及任意執行緒、執行緒組的操作都需要對這些執行緒執行緒組的許可權進行檢查,若無許可權都會丟擲“SecurityException”。
-
int enumerate(Thread[] list):將此執行緒組及其子組中的每個活動執行緒複製到指定的陣列中。相當於enumerate(Thread[] list, true)
-
int enumerate(Thread[] list, boolean recurse):若recurse為true,遞迴將此執行緒組中的每個活動執行緒複製到指定的陣列中。
-
int enumerate(ThreadGroup[] list):複製到該執行緒組及其子組中每個活動子組的指定陣列引用。
-
int enumerate(ThreadGroup[] list, boolean recurse):複製到該執行緒組中每個活動子組的指定陣列引用。
-
void list():將有關此執行緒組的資訊列印到標準輸出。
-
String toString():返回此Thread組的字串表示形式。
-
void uncaughtException(Thread t, Throwable e):當此執行緒組中的執行緒因為一個未捕獲的異常由Java Virtual Machine呼叫,而執行緒不具有特定Thread.UncaughtExceptionHandler安裝。
-
void resume():已棄用,這種方法僅與Thread.suspend和ThreadGroup.suspend一起使用 ,這兩種方法都已經被棄用,因為它們本身就是死鎖的。 詳見Thread.suspend() 。
-
void stop():已棄用,這種方法本質上是不安全的。 詳見Thread.stop() 。
-
void suspend():已棄用,這種方法本質上是死鎖的。 詳見Thread.suspend() 。
-
boolean allowThreadSuspension(boolean b):已棄用,此呼叫的定義取決於suspend() ,它已被棄用。 此外,從未指定此呼叫的行為。
4.4 測試程式碼
程式碼
public void test1(){ System.out.println(String.format("主執行緒:獲取主執行緒的父執行緒組名字%s",Thread.currentThread().getThreadGroup().getParent().getName())); /* 建立執行緒組樹 */ System.out.println("*****建立執行緒組樹1作為2、3父項,2作為4父項"); ThreadGroup threadGroup1 = new ThreadGroup("執行緒組1"); ThreadGroup threadGroup2 = new ThreadGroup(threadGroup1,"執行緒組2"); ThreadGroup threadGroup3 = new ThreadGroup(threadGroup1,"執行緒組3"); ThreadGroup threadGroup4 = new ThreadGroup(threadGroup2,"執行緒組4"); System.out.println(String.format("主執行緒:執行緒組1是否是執行緒組2的父執行緒組%b",threadGroup1.parentOf(threadGroup2))); System.out.println(String.format("主執行緒:執行緒組1是否是執行緒組4的父執行緒組%b",threadGroup1.parentOf(threadGroup4))); System.out.println(String.format("主執行緒:執行緒組3是否是執行緒組4的父執行緒組%b",threadGroup3.parentOf(threadGroup4))); /* 建立執行緒組中的子執行緒,執行緒組1:1,執行緒組2:1,執行緒組3:1,執行緒組4:3 */ System.out.println("*****建立執行緒組中的子執行緒,執行緒組1:1,執行緒組2:1,執行緒組3:1,執行緒組4:3,並執行"); Thread[] threads = new Thread[6]; threads[0]= new myThread(threadGroup1,"執行緒組1-執行緒1"); threads[1]= new myThread(threadGroup2,"執行緒組2-執行緒2"); threads[2]= new myThread(threadGroup3,"執行緒組3-執行緒3"); threads[3]= new myThread(threadGroup4,"執行緒組4-執行緒41"); threads[4]= new myThread(threadGroup4,"執行緒組4-執行緒42"); threads[5]= new myThread(threadGroup4,"執行緒組4-執行緒43"); for(int i=0;i<6;i++){ threads[i].start(); } /* 將執行緒組1,2,3都設定為守護執行緒組 */ System.out.println("*****將執行緒組1,2,3都設定為守護執行緒組"); threadGroup1.setDaemon(true); threadGroup2.setDaemon(true); threadGroup3.setDaemon(true); System.out.println(String.format("主執行緒:執行緒組1是否是守護執行緒%b",threadGroup1.isDaemon())); System.out.println(String.format("主執行緒:執行緒組3是否是守護執行緒%b",threadGroup3.isDaemon())); System.out.println(String.format("主執行緒:執行緒組4是否是守護執行緒%b",threadGroup4.isDaemon())); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } /* 複製執行緒組先全部子執行緒到陣列*/ System.out.println("*****複製執行緒組先全部活動子執行緒到陣列並遍歷"); Thread[] threads2 = new Thread[threadGroup1.activeCount()]; int tc = threadGroup1.enumerate(threads2); System.out.println(String.format("主執行緒:執行緒組1中%d個子執行緒成功複製到執行緒陣列threads",tc)); for(int i=0;i<tc;i++){ System.out.println(threads2[i]); } /* 中斷全部等待的子執行緒 */ System.out.println("*****中斷全部等待的子執行緒"); threadGroup1.interrupt(); for(int i=0;i<tc;i++){ try { threads2[i].join(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("*****所有子執行緒執行完畢並銷燬"); System.out.println(String.format("主執行緒:執行緒組1下執行執行緒估計%d",threadGroup1.activeCount())); System.out.println(String.format("主執行緒:執行緒組1下執行執行緒組估計%d",threadGroup1.activeGroupCount())); System.out.println("*****執行緒組1,2,3作為守護執行緒,執行緒組3會自動銷燬,1和2會因為4無法銷燬"); System.out.println(String.format("主執行緒:執行緒組1是否被銷燬%b",threadGroup1.isDestroyed())); System.out.println(String.format("主執行緒:執行緒組3是否被銷燬%b",threadGroup3.isDestroyed())); System.out.println(String.format("主執行緒:執行緒組4是否被銷燬%b",threadGroup4.isDestroyed())); System.out.println("*****手動銷燬執行緒組4"); threadGroup4.destroy(); System.out.println(String.format("主執行緒:執行緒組1是否被銷燬%b",threadGroup1.isDestroyed())); } class myThread extends Thread{ public myThread(ThreadGroup tg,String name){ super(tg,name); } @Override public void run(){ synchronized ("A"){ System.out.println(String.format("子執行緒 %s:呼叫wait()進入休眠",getName())); try { "A".wait(); } catch (InterruptedException e) { System.out.println(String.format("子執行緒 %s:被中斷!",getName())); } } } }
執行結果
主執行緒:獲取主執行緒的父執行緒組名字system *****建立執行緒組樹1作為2、3父項,2作為4父項 主執行緒:執行緒組1是否是執行緒組2的父執行緒組true 主執行緒:執行緒組1是否是執行緒組4的父執行緒組true 主執行緒:執行緒組3是否是執行緒組4的父執行緒組false *****建立執行緒組中的子執行緒,執行緒組1:1,執行緒組2:1,執行緒組3:1,執行緒組4:3,並執行 *****將執行緒組1,2,3都設定為守護執行緒組 主執行緒:執行緒組1是否是守護執行緒true 子執行緒 執行緒組1-執行緒1:呼叫wait()進入休眠 主執行緒:執行緒組3是否是守護執行緒true 子執行緒 執行緒組4-執行緒42:呼叫wait()進入休眠 主執行緒:執行緒組4是否是守護執行緒false 子執行緒 執行緒組3-執行緒3:呼叫wait()進入休眠 子執行緒 執行緒組4-執行緒41:呼叫wait()進入休眠 子執行緒 執行緒組2-執行緒2:呼叫wait()進入休眠 子執行緒 執行緒組4-執行緒43:呼叫wait()進入休眠 *****複製執行緒組先全部活動子執行緒到陣列並遍歷 主執行緒:執行緒組1中6個子執行緒成功複製到執行緒陣列threads Thread[執行緒組1-執行緒1,5,執行緒組1] Thread[執行緒組2-執行緒2,5,執行緒組2] Thread[執行緒組4-執行緒41,5,執行緒組4] Thread[執行緒組4-執行緒42,5,執行緒組4] Thread[執行緒組4-執行緒43,5,執行緒組4] Thread[執行緒組3-執行緒3,5,執行緒組3] *****中斷全部等待的子執行緒 子執行緒 執行緒組1-執行緒1:被中斷! 子執行緒 執行緒組3-執行緒3:被中斷! 子執行緒 執行緒組4-執行緒41:被中斷! 子執行緒 執行緒組4-執行緒42:被中斷! 子執行緒 執行緒組4-執行緒43:被中斷! 子執行緒 執行緒組2-執行緒2:被中斷! *****所有子執行緒執行完畢並銷燬 主執行緒:執行緒組1下執行執行緒估計0 主執行緒:執行緒組1下執行執行緒組估計2 *****執行緒組1,2,3作為守護執行緒,執行緒組3會自動銷燬,1和2會因為4無法銷燬 主執行緒:執行緒組1是否被銷燬false 主執行緒:執行緒組3是否被銷燬true 主執行緒:執行緒組4是否被銷燬false *****手動銷燬執行緒組4 主執行緒:執行緒組1是否被銷燬true
解析
-
主執行緒main所屬執行緒組的父執行緒組是system
-
建立了執行緒組樹如下
-
建立了6個子執行緒,結構如下,並執行所有子執行緒,所有子執行緒進入wait狀態,使得所有子執行緒在下面的程式碼執行時一直處於活動狀態
-
將執行緒組1、2、3都設定為守護執行緒組,執行緒組4為非守護執行緒組
-
呼叫threadGroup1.enumerate(threads2)成功將執行緒組1及其子組下全部活動執行緒複製到threads2陣列中
-
通過interrupt中斷執行緒組1及其子組下全部執行緒
-
所有子執行緒執行完畢銷燬後,作為守護執行緒,執行緒組3直接銷燬,執行緒組4不是守護執行緒所以不會自動銷燬,執行緒組1,2因為還有子執行緒組未銷燬,不會自動銷燬
-
手動銷燬執行緒組4,執行緒組1、2自動銷燬
4.X 參考
當你深入瞭解,你就會發現世界如此廣袤,而你對世界的瞭解則是如此淺薄,請永遠保持謙卑的態度。