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則相反。