六. 线程管理
6.1 线程组
类似于在计算机中使用文件夹管理文件, 也可以使用线程组来管理线程,在线程组中定义一组相似(相关)的线程. 在线程组中也可以定义子线程组,
Thread 类有几个构造方法允许在创建线程时指定线程组,如果创建线程时没有指定线程组则该线程就属于浮现出所在的线程组, JVM 在创建main 线程时会为他指定一个线程组,因此每个java线程都有一个线程组与之关联,可以调用线程getThreadGroup方法返回线程组.
线程组开始时处于安全的考虑设计用来区分不同的Applet,然而ThreadGroup 并未实现这一目标 ,在新开发的系统中,已经不常用线程组.
现在一般会将一组相关的线程存入一个数组或一个集合中, 如果仅仅是用来区分线程时,可以使用线程名称来区分. 多数情况下,可以忽略线程组.
6.1.1 创建线程组
package com.company.threadgroup;/*** 演示创建线程组,*/
public class Test01 {public static void main(String[] args) {// 1.返回当前main 线程的线程组ThreadGroup mianGroup = Thread.currentThread().getThreadGroup();System.out.println(mianGroup.toString()); // java.lang.ThreadGroup[name=main,maxpri=10]// 2.定义线程组,如果不指定所属线程组,则自动归属到当先线程所属的线程组中ThreadGroup group1 = new ThreadGroup("group1");System.out.println(group1); // java.lang.ThreadGroup[name=group1,maxpri=10]// 3. 定义线程组,同时指定父线程组ThreadGroup group2 = new ThreadGroup(mianGroup, "group2");// 现在group1 与group2 都是mainGroup 线程组中的子线程组System.out.println(group1.getParent() == mianGroup); // trueSystem.out.println(group2.getParent() == mianGroup); // true// 4 在创建线程时指定所属线程组Runnable r = new Runnable() {@Overridepublic void run() {System.out.println(Thread.currentThread());}};// 在创建线程时, 如果没有指定线程组,则默认线程归属到父线程的线程组中。// 在main 线程中 创建了t1 线程,称main线程为父线程,t1线程称为子线程,t1没有指定线程组,则t1 线程就归属与父线程的线程组中Thread t1 = new Thread(r, "t1");System.out.println(t1); // Thread[t1,5,main] t1的线程组是main// 创建线程时,可以指定线程所属线程组Thread t2 = new Thread(group1, r, "t2");Thread t3 = new Thread(group2, r, "t3");System.out.println(t2); // Thread[t2,5,group1]System.out.println(t3); // Thread[t3,5,group2]}
}
6.1.2 线程组的基本操作
activeCount() 返回当前线程组及子线程组中活动线程的数量(近似值)
activeGroupCount() 返回当前线程组及子线程组中的数量(近似值)
int enumerate(Threat[] list) 将当前线程组中的活动线程复制到参数数组中。
enumerate(ThreadGroup[] list)将当前线程组中的活动线程组复制到参数数组中。
getMaxPriotity 返回线程组的最大优先级,默认是10.
getName() 返回线程组的名称
getParent()返回父线程组
interrupt() 中断线程组中所有的线程
isDaemon() 判断当前线程组是否为守护线程
list() 将当前线程组中的活动线程打印出来。
parentOf(ThreadGroup g) 判断当前线程组中是否为参数线程组的父线程组。
setDaemon(boolean daemon)设置线程组为守护线程组
/*** 演示线程组的基本操作*/
public class Test02 {public static void main(String[] args) {ThreadGroup mianGroup = Thread.currentThread().getThreadGroup(); // 返回当前线程组// 在定义线程组ThreadGroup group = new ThreadGroup("group"); // 默认group的父线程组是main线程组Runnable r = new Runnable() {@Overridepublic void run() {while (true) {System.out.println("------- 当前线程" + Thread.currentThread());try{Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}};Thread t1 = new Thread(r,"t1"); // 默认在main线程组中创建线程Thread t2 = new Thread(group,"t2"); // 在指定的group 线程组中创建线程t1.start();t2.start();System.out.println("main 线程组中活动线程数量:" + mianGroup.activeCount()); // 4, main线程组中活动线程: main,t1,t2, 垃圾回收器System.out.println("group 子线程组中活动线程数量:" + group.activeCount()); // 1, t2System.out.println("main 线程组中子线程组数量:" + mianGroup.activeGroupCount()); //1 groupSystem.out.println("group 子线程组中子线程组数量:" + group.activeGroupCount());// 0System.out.println("main 线程组的父线程组:" + mianGroup.getParent()); // mian 线程组的父线程组是systemSystem.out.println("group 线程组的父线程组:" + group.getParent()); // mainSystem.out.println(mianGroup.parentOf(mianGroup)); // true , 线程组也是他自己的父线程组System.out.println(mianGroup.parentOf(group));// truemianGroup.list(); // 把main 线程组中所有的线程打印输出}
}
6.1.3 复制线程组中的线程及子线程组
enumerate(Thread[] list) 把当前线程组和子线程组中所有的线程复制到参数数组中;
enumerate(Thread[] list,boolean recursive) 如果第二个参数设置为false ,则只复制当前线程组中所有的线程,不复制子线程组中的线程
enumerate (ThreadGroup[] list) 把当前线程组和子线程组中所有的线程组复制到参数数组中;
enumerate (ThreadGroup[] list, boolean recursive)第二个参数设置为false,则只复制当前线程组的子线程组。
/*** 演示复制线程组中的内容*/
public class Test03 {public static void main(String[] args) {ThreadGroup mainGroup = Thread.currentThread().getThreadGroup(); // 返回main线程的main线程组// 创建线程组ThreadGroup group1 = new ThreadGroup("group1"); // 默认group1的父线程组就是当前线程的mainThreadGroup group2 = new ThreadGroup(mainGroup,"group1"); // 默认group1的父线程组就是当前线程的mainRunnable runnable = new Runnable() {@Overridepublic void run() {System.out.println(" -- 当前线程:" + Thread.currentThread());try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}};// 创建三个线程Thread t1 = new Thread(runnable,"t1"); // 默认在main 线程组中创建线程Thread t2 = new Thread(group1, runnable,"t2"); // 在group1 线程组中创建线程Thread t3 = new Thread(group2, runnable,"t3"); // 在group2 线程组中创建线程t1.start();t2.start();t3.start();//1、 把main 线程组中的线程复制到数组中// 先定义存储线程的数组,数组的长度为main线程组中活动线程的数量Thread[] threadList = new Thread[mainGroup.activeCount()];
// //把main 线程组中包括子线程组中的所有的线程复制到数组中
// mainGroup.enumerate(threadList);
// for (Thread thread:threadList
// ) {
// System.out.println(thread);
// /*
// 输出:
// Thread[Monitor Ctrl-Break,5,main]
// Thread[t1,5,main]
// Thread[t2,5,group1]
// Thread[t3,5,group1]
// */
// }// 只把main 线程组中的线程复制到数组中,不包含子线程组中的线程System.out.println("----------------");mainGroup.enumerate(threadList, false);for (Thread thread:threadList) {System.out.println(thread);/*输出:Thread[Monitor Ctrl-Break,5,main]Thread[t1,5,main]Thread[t2,5,group1]Thread[t3,5,group1]*/}// 2) 把main 线程组中的子线程组复制到数组中// 定义数组存储线程组ThreadGroup[] threadGroups = new ThreadGroup[mainGroup.activeCount()];// 把main 线程在中的子线程组复制到数组中mainGroup.enumerate(threadGroups);System.out.println("==================");for(ThreadGroup threadGroup: threadGroups) {System.out.println(threadGroup);/*** ava.lang.ThreadGroup[name=group1,maxpri=10]* java.lang.ThreadGroup[name=group1,maxpri=10]* null* null*/}}
}
6.1.4 线程组的批量中断
线程组中interrupt()方法,可以给该线程组中所有的活动线程添加中断标识
如果中断睡眠中的线程,产生中断异常, 同时会清除中断标识
/*** 线程组的批量中断*/
public class Test04 {public static void main(String[] args) throws InterruptedException {Runnable r = new Runnable() {@Overridepublic void run() {System.out.println("当前线程---" + Thread.currentThread() + "-- 开始循环");// 当线程没有被中断就一直循环while (!Thread.currentThread().isInterrupted()) {System.out.println(Thread.currentThread().getName() + "===");
// try {
// Thread.sleep(2000);
// } catch (InterruptedException e) {
// // 如果中断睡眠中的线程,产生中断异常, 同时会清除中断标识
// e.printStackTrace();
// }}System.out.println