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 参考
0、JAVA多线程编程
Java多线程编程所涉及的知识点包含线程创建、线程同步、线程间通信、线程死锁、线程控制(挂起、停止和恢复)。之前
-
篇0
-
篇1
-
篇2
-
篇3
-
篇4
-
篇5
-
篇6
-
篇7
-
篇8