线程组的使用
1.线程组的概念
可以把线程归属到某一个线程组中,线程组中可以有线程对象,也可以有线程组,组中还可以用线程。这样的组织结构有点类似于树的形式。
线程组的作用是,可以批量的管理线程或者线程组对象,有效地对线程或线程组对象进行组织。
2.线程对象关联线程组:1级关联
1级关联就是父对象中有子对象,但并不创建子孙对象。比如创建一些线程,为了有效地对这些线程进行组织管理,通常的做法就是创建一个线程组,然后将部分线程归属到该组中。这样可以对零散的线程对象进行有效的组织与规划。
package cn.qlq.thread.sixteeen; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Demo1 implements Runnable { private static final Logger LOGGER = LoggerFactory.getLogger(Demo1.class); @Override public void run() { LOGGER.info("threadname ->{},threadGroup ->{}", Thread.currentThread().getName(), Thread.currentThread().getThreadGroup().getName()); } public static void main(String[] args) { ThreadGroup threadGroup = new ThreadGroup("g1"); new Thread(threadGroup, new Demo1()).start(); new Thread(threadGroup, new Demo1()).start(); } }
结果:
18:53:40 [cn.qlq.thread.sixteeen.Demo1]-[INFO] threadname ->Thread-1,threadGroup ->g1
18:53:40 [cn.qlq.thread.sixteeen.Demo1]-[INFO] threadname ->Thread-0,threadGroup ->g1
3.线程对象关联线程组:多级关联
多级关联就是父对象中有子对象,子对象中有子孙对象。不支持这种写法,不利于管理。
例如:在main所在的线程组中增加一个subMain线程组,并在subMain线程组增加一个线程。
package cn.qlq.thread.sixteeen; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Demo2 { private static final Logger LOGGER = LoggerFactory.getLogger(Demo2.class); public static void main(String[] args) { LOGGER.info("main线程组,名字为:{}", Thread.currentThread().getThreadGroup().getName()); ThreadGroup threadGroup = Thread.currentThread().getThreadGroup(); ThreadGroup threadGroup2 = new ThreadGroup(threadGroup, "subMain"); new Thread(threadGroup2, new Runnable() { @Override public void run() { LOGGER.info("threadname ->{},threadGroup ->{}", Thread.currentThread().getName(), Thread.currentThread().getThreadGroup().getName()); try { Thread.sleep(2 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); ThreadGroup[] threadGroups = new ThreadGroup[Thread.currentThread().getThreadGroup().activeGroupCount()]; Thread.currentThread().getThreadGroup().enumerate(threadGroups); LOGGER.info("main线程有{}线程组,名字为:{}", threadGroups.length, threadGroups[0].getName()); Thread[] threads = new Thread[threadGroups[0].activeCount()]; threadGroups[0].enumerate(threads); LOGGER.info("" + threads[0].getName()); } }
结果:(可以看出main线程所处的线程组名字为main)
19:16:36 [cn.qlq.thread.sixteeen.Demo2]-[INFO] main线程组,名字为:main
19:16:36 [cn.qlq.thread.sixteeen.Demo2]-[INFO] threadname ->Thread-0,threadGroup ->subMain
19:16:36 [cn.qlq.thread.sixteeen.Demo2]-[INFO] main线程有1线程组,名字为:subMain
19:16:36 [cn.qlq.thread.sixteeen.Demo2]-[INFO] Thread-0
注意:activeCount()和activeGroupCount()的数目不是固定的,是系统中环境的一个快照。方法enumerate的作用是复制线程或者线程组,可以理解为将树形结构变为水平的数组结构。
4.线程自动归属
如果没有指定,默认创建的线程的线程组是当前线程所处的线程组;如果创建一个线程组没有指定,线程组的父线程组默认是当前线程锁处的线程组。
package cn.qlq.thread.sixteeen; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Demo3 { private static final Logger LOGGER = LoggerFactory.getLogger(Demo3.class); public static void main(String[] args) { LOGGER.info("main线程组,名字为:{}", Thread.currentThread().getThreadGroup().getName()); new Thread(new Runnable() { @Override public void run() { LOGGER.info("threadname ->{},threadGroup ->{}", Thread.currentThread().getName(), Thread.currentThread().getThreadGroup().getName()); try { Thread.sleep(2 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); ThreadGroup threadGroup2 = new ThreadGroup("subMain"); LOGGER.info("threadGroup2线程组的父线程组是->{}", threadGroup2.getParent().getName()); } }
结果:
19:20:53 [cn.qlq.thread.sixteeen.Demo3]-[INFO] main线程组,名字为:main
19:20:53 [cn.qlq.thread.sixteeen.Demo3]-[INFO] threadGroup2线程组的父线程组是->main
19:20:53 [cn.qlq.thread.sixteeen.Demo3]-[INFO] threadname ->Thread-0,threadGroup ->main
5.获取根线程组
根线程组是system,如果继续获取system.getParent()的话会报错空指针异常。
package cn.qlq.thread.sixteeen; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Demo4 { private static final Logger LOGGER = LoggerFactory.getLogger(Demo4.class); public static void main(String[] args) { LOGGER.info("main函数所在的线程组,名字为:{}", Thread.currentThread().getThreadGroup().getName()); LOGGER.info("main线程组的父线程组名字为:{}", Thread.currentThread().getThreadGroup().getParent().getName()); LOGGER.info("main线程组的父线程组的父线程组的名字为:{}", Thread.currentThread().getThreadGroup().getParent().getParent().getName()); } }
结果:
19:24:37 [cn.qlq.thread.sixteeen.Demo4]-[INFO] main函数所在的线程组,名字为:main
Exception in thread "main" 19:24:37 [cn.qlq.thread.sixteeen.Demo4]-[INFO] main线程组的父线程组名字为:system
java.lang.NullPointerException
at cn.qlq.thread.sixteeen.Demo4.main(Demo4.java:13)
6.线程组里面加线程组
用显示的方式在当前main线程组中增加一个main2线程组。
package cn.qlq.thread.sixteeen; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Demo5 { private static final Logger LOGGER = LoggerFactory.getLogger(Demo5.class); public static void main(String[] args) { LOGGER.info("线程组名字为:{},父线程组名称:{}", Thread.currentThread().getThreadGroup().getName(), Thread.currentThread().getThreadGroup().getParent().getName()); LOGGER.info("线程组中活动的线程数量为:{}", Thread.currentThread().getThreadGroup().activeCount()); LOGGER.info("线程组中活动的线程组数量为:{}---加之前", Thread.currentThread().getThreadGroup().activeGroupCount()); // 添加一个线程到当前线程组中 ThreadGroup threadGroup = new ThreadGroup(Thread.currentThread().getThreadGroup(), "main2"); LOGGER.info("线程组中活动的线程组数量为:{}---加之后", Thread.currentThread().getThreadGroup().activeGroupCount()); // 将当前线程组复制到新创建的线程组数组中 ThreadGroup[] threadGroups = new ThreadGroup[Thread.currentThread().getThreadGroup().activeGroupCount()]; Thread.currentThread().getThreadGroup().enumerate(threadGroups); for (ThreadGroup threadGroup2 : threadGroups) { LOGGER.info("" + threadGroup2.getName()); } } }
结果:
21:49:09 [cn.qlq.thread.sixteeen.Demo5]-[INFO] 线程组名字为:main,父线程组名称:system
21:49:09 [cn.qlq.thread.sixteeen.Demo5]-[INFO] 线程组中活动的线程数量为:1
21:49:09 [cn.qlq.thread.sixteeen.Demo5]-[INFO] 线程组中活动的线程组数量为:0---加之前
21:49:09 [cn.qlq.thread.sixteeen.Demo5]-[INFO] 线程组中活动的线程组数量为:1---加之后
21:49:09 [cn.qlq.thread.sixteeen.Demo5]-[INFO] main2
7.批量停止线程组内的线程
调用threadGroup.interrupt();会向组内正在运行的线程发出中断信号。
package cn.qlq.thread.sixteeen; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Demo6 { private static final Logger LOGGER = LoggerFactory.getLogger(Demo6.class); public static void main(String[] args) throws InterruptedException { ThreadGroup threadGroup = new ThreadGroup(Thread.currentThread().getThreadGroup(), "main2"); LOGGER.info("线程组中活动的线程组数量为:{}---加之后", Thread.currentThread().getThreadGroup().activeGroupCount()); for (int i = 0; i < 5; i++) { new Thread(threadGroup, new Runnable() { @Override public void run() { LOGGER.info("threadName->{}准备死循环", Thread.currentThread().getName()); while (!Thread.currentThread().isInterrupted()) { } LOGGER.info("threadName->{}结束死循环", Thread.currentThread().getName()); } }).start(); } Thread.sleep(2 * 1000); // 向线程组发出终端信号 threadGroup.interrupt(); } }
结果:
21:56:30 [cn.qlq.thread.sixteeen.Demo6]-[INFO] 线程组中活动的线程组数量为:1---加之后
21:56:30 [cn.qlq.thread.sixteeen.Demo6]-[INFO] threadName->Thread-0准备死循环
21:56:30 [cn.qlq.thread.sixteeen.Demo6]-[INFO] threadName->Thread-1准备死循环
21:56:30 [cn.qlq.thread.sixteeen.Demo6]-[INFO] threadName->Thread-2准备死循环
21:56:30 [cn.qlq.thread.sixteeen.Demo6]-[INFO] threadName->Thread-3准备死循环
21:56:30 [cn.qlq.thread.sixteeen.Demo6]-[INFO] threadName->Thread-4准备死循环
21:56:32 [cn.qlq.thread.sixteeen.Demo6]-[INFO] threadName->Thread-2结束死循环
21:56:32 [cn.qlq.thread.sixteeen.Demo6]-[INFO] threadName->Thread-1结束死循环
21:56:32 [cn.qlq.thread.sixteeen.Demo6]-[INFO] threadName->Thread-0结束死循环
21:56:32 [cn.qlq.thread.sixteeen.Demo6]-[INFO] threadName->Thread-3结束死循环
21:56:32 [cn.qlq.thread.sixteeen.Demo6]-[INFO] threadName->Thread-4结束死循环
看源码发现threadGroup.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(); } }
8.递归与非递归获得组内对象
8.1递归与非递归获得组内的组
向main组内增加main2组,main2组中中增加组main22,main22组中增加组main222
package cn.qlq.thread.sixteeen; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Demo7 { private static final Logger LOGGER = LoggerFactory.getLogger(Demo7.class); public static void main(String[] args) throws InterruptedException { // 向main组内增加main2组,main2中增加main22,main22中增加main222 ThreadGroup threadGroup2 = new ThreadGroup(Thread.currentThread().getThreadGroup(), "main2"); ThreadGroup threadGroup22 = new ThreadGroup(threadGroup2, "main22"); ThreadGroup threadGroup222 = new ThreadGroup(threadGroup22, "main222"); LOGGER.info("==========={}=======", Thread.currentThread().getThreadGroup().activeGroupCount()); // 分配空间,不一定全部用完 ThreadGroup threadGroups1[] = new ThreadGroup[Thread.currentThread().getThreadGroup().activeGroupCount()]; ThreadGroup threadGroups2[] = new ThreadGroup[Thread.currentThread().getThreadGroup().activeGroupCount()]; // 传入true是递归获取其子孙组 Thread.currentThread().getThreadGroup().enumerate(threadGroups1, true); for (ThreadGroup t : threadGroups1) { LOGGER.info("threadGroupName->{}", t.getName()); } // 传入false是只获取直属的线程组 LOGGER.info("==========={}=======", Thread.currentThread().getThreadGroup().activeGroupCount()); Thread.currentThread().getThreadGroup().enumerate(threadGroups2, false); for (int i = 0; i < threadGroups2.length; i++) { if (threadGroups2[i] != null) { LOGGER.info("threadGroupName->{}", threadGroups2[i].getName()); } else { LOGGER.info("{}为null", i); } } } }
结果:
22:09:13 [cn.qlq.thread.sixteeen.Demo7]-[INFO] ===========3=======
22:09:13 [cn.qlq.thread.sixteeen.Demo7]-[INFO] threadGroupName->main2
22:09:13 [cn.qlq.thread.sixteeen.Demo7]-[INFO] threadGroupName->main22
22:09:13 [cn.qlq.thread.sixteeen.Demo7]-[INFO] threadGroupName->main222
22:09:13 [cn.qlq.thread.sixteeen.Demo7]-[INFO] ===========3=======
22:09:13 [cn.qlq.thread.sixteeen.Demo7]-[INFO] threadGroupName->main2
22:09:13 [cn.qlq.thread.sixteeen.Demo7]-[INFO] 1为null
22:09:13 [cn.qlq.thread.sixteeen.Demo7]-[INFO] 2为null
8.2 递归与非递归获取组内的线程
向main组内增加一个线程,增加一个组main2,并且向main2组中增加1个线程(相当于main组的子孙线程)。
package cn.qlq.thread.sixteeen; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Demo8 extends Thread { @Override public void run() { while (true) { } } private static final Logger LOGGER = LoggerFactory.getLogger(Demo8.class); public static void main(String[] args) throws InterruptedException { Thread t0 = new Thread(new Demo8()); t0.start(); // 向main组内增加main2组和一个线程对象 ThreadGroup threadGroup2 = new ThreadGroup(Thread.currentThread().getThreadGroup(), "main2"); Thread t1 = new Thread(threadGroup2, new Demo8()); t1.start(); LOGGER.info("==========={}=======", Thread.currentThread().getThreadGroup().activeCount()); // 分配空间,不一定全部用完 Thread thread1[] = new Thread[Thread.currentThread().getThreadGroup().activeCount()]; Thread thread2[] = new Thread[Thread.currentThread().getThreadGroup().activeCount()]; // 传入true是递归获取其子孙线程 Thread.currentThread().getThreadGroup().enumerate(thread1, true); for (Thread t : thread1) { LOGGER.info("threadName->{}", t.getName()); } // 传入false是只获取直属的线程 LOGGER.info("==========={}=======", Thread.currentThread().getThreadGroup().activeCount()); Thread.currentThread().getThreadGroup().enumerate(thread2, false); for (int i = 0; i < thread2.length; i++) { if (thread2[i] != null) { LOGGER.info("threadName->{}", thread2[i].getName()); } else { LOGGER.info("{}为null", i); } } } }
结果:
22:24:32 [cn.qlq.thread.sixteeen.Demo8]-[INFO] ===========3=======
22:24:32 [cn.qlq.thread.sixteeen.Demo8]-[INFO] threadName->main
22:24:32 [cn.qlq.thread.sixteeen.Demo8]-[INFO] threadName->Thread-1
22:24:32 [cn.qlq.thread.sixteeen.Demo8]-[INFO] threadName->Thread-3
22:24:32 [cn.qlq.thread.sixteeen.Demo8]-[INFO] ===========3=======
22:24:32 [cn.qlq.thread.sixteeen.Demo8]-[INFO] threadName->main
22:24:32 [cn.qlq.thread.sixteeen.Demo8]-[INFO] threadName->Thread-1
22:24:32 [cn.qlq.thread.sixteeen.Demo8]-[INFO] 2为null
总结:
1. threadGroup.activeGroupCount()是返回线程组的子孙线程组,包括非直属的线程组。
2. threadGroup.enumerate(ThreadGroup list[], boolean recurse)是将threadGroup组内的组复制到list数组中,第二个参数是是否递归(也就是是否包含子孙),默认的是true。也就是此方法可以将一个树形的组结构变为一个水平的数组结构。源码如下:
public int enumerate(ThreadGroup list[]) { checkAccess(); return enumerate(list, 0, true); } public int enumerate(ThreadGroup list[], boolean recurse) { checkAccess(); return enumerate(list, 0, recurse); } private int enumerate(ThreadGroup list[], int n, boolean recurse) { int ngroupsSnapshot = 0; ThreadGroup[] groupsSnapshot = null; synchronized (this) { if (destroyed) { return 0; } int ng = ngroups; if (ng > list.length - n) { ng = list.length - n; } if (ng > 0) { System.arraycopy(groups, 0, list, n, ng); n += ng; } 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; }
3. threadGroup.activeCount()是返回线程组的子孙线程,包括非直属的线程。
4. threadGroup.enumerate(Thread list[], boolean recurse)是将threadGroup组内的线程复制到list数组中,第二个参数是是否递归(也就是是否包含子孙),默认的是true。也就是此方法可以将一个树形的线程结构变为一个水平的数组结构。源码如下:
public int enumerate(Thread list[]) { checkAccess(); return enumerate(list, 0, true); } public int enumerate(Thread list[], boolean recurse) { checkAccess(); return enumerate(list, 0, recurse); } 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; }