顾名思义,线程组是对线程进行分组。Java规定,线程组中可以包括若干线程和线程组。线程组中的线程可以访问本线程组,但不可以越过线程组的限制再访问外层线程或者线程组。这样可以将线程的操作限制在给定范围内。
每个线程都隶属于唯一的线程组,这个线程组在线程创建时指定并在线程的整个生命期内都不能更改。可以通过调用包含ThreadGroup类型参数的Thread类构造函数来指定线程隶属的线程组;若没有指定,则线程默认地隶属于名为system的系统线程组。
在Java中,除了预建的系统线程组外,所有线程组都必须显式创建。
在Java中,除系统线程组外的每个线程组又隶属于另一个线程组。可以在创建线程组时指定其所隶属的线程组;若没有指定,则默认地隶属于系统线程组。这样,所有线程组组成了一棵以系统线程组为根的树。
Java允许对一个线程组中的所有线程同时进行操作,例如,可以通过调用线程组的相应方法来设置其中所有线程的优先级,也可以启动或阻塞其中的所有线程。
线程组机制的另一个重要作用是线程安全。线程组机制允许通过分组来区分有不同安全特性的线程,对不同组的线程进行不同的处理,还可以通过线程组的分层结构来支持不对等安全措施的采用。Java 的ThreadGroup 类提供了大量的方法来方便对线程组树中的每一个线程组以及线程组中的每一个线程进行操作。
【例9-15】本例通过一个程序来说明线程组的创建和它的一些方法的使用,程序代码如下。
import java.lang.*;
import java.io.*;
public class ThreadGroupDemo{
public static void main(String[] args){
ThreadGroup grp1=new ThreadGroup("Group1");
ThreadGroup grp2=new ThreadGroup("Group2");
System.out.println("Group1: "+grp1.toString());
Thread thread1=new Thread(grp1,new AThread());
ThreadGroup parent=grp1.getParent();
System.out.println("Group1's parent: " + parent);
System.out.println("ParentGroup's info:");
parent.list();
System.out.println("ActiveGroupCount in ParentGroup: "
+ parent.activeGroupCount());
System.out.println("ActiveCount in ParentGroup: "
+ parent.activeCount());
thread1.start();
System.out.println("Current thread's Group: "
+ Thread.currentThread().getThreadGroup());
}
}
class AThread extends Thread{
public AThread(){
super();
}
public void run(){
System.out.println("I am Thread: " + this.toString());
}
}
程序是这样工作的:先创建两个ThreadGroup对象,再在第一个ThreadGroup对象中创建一个线程,然后就是打印一些信息。先输出了第一个ThreadGroup对象的有关信息,再显示其父线程组的信息,然后调用list( )方法打印该父线程组中各级成员列表,然后调用activeGroupCount( )方法和activeCount( )方法统计了列表中线程组与线程的数目,之后是当前线程的所在线程组描述,最后是关于一个线程的信息。
程序初始创建了线程组对象,构造函数的参数为线程组名。ThreadGroup类另外还有一个构造函数:
public ThreadGroup(ThreadGroup parent,String name)
它比前一种形式多一个参数指明该线程组的父线程组。程序创建了一个线程。这个构造函数看来也许有些奇特,它的形式如下。
public Thread(ThreaqdGroup group,Runnable target)
意即创建这样一个线程:它属于线程组gruop,并利用target的run( )方法。例子中target是一个AThread对象,而AThread是Thread的子类,由于Thread类实现了Runnable接口,因此AThread的对象target就可以作为Runnable的一个实例。这里再解释一下为什么会有Thread-0和Thread-1两个线程。其中一个是我们创建并使之运行的thread1,另一个就是程序中new AThread( )的结果:
Thread thread1=new Thread(grp1,new AThread());
程序的输出如下:
Group1: java.lang.ThreadGroup[name=Group1,maxpri=10]
Group1's parent: java.lang.ThreadGroup[name=main,maxpri=10]
ParentGroup's info:
java.lang.ThreadGroup[name=main,maxpri=10]
Thread[main,5,main]
Thread[Thread-0,5,main]
java.lang.ThreadGroup[name=Group1,maxpri=10]
Thread[Thread-1,5,Group1]
java.lang.ThreadGroup[name=Group2,maxpri=10]
ActiveGroupCount in ParentGroup: 2
ActiveCount in ParentGroup: 3
Current thread's Group: java.lang.ThreadGroup[name=main,maxpri=10]
I am Thread: Thread[Thread-0,5,main]
线程组类的方法与线程类有不少相似之处。如getName()得到线程组的名字。isDaemon()判断线程组是否为守护线程组。setDaemon( )设置线程组为守护线程组(参数为true)或用户线程组(参数为false)。checkAccess( )判断线程是否允许被更改。
parentOf(ThreadGroup g)方法可以用于判断这个线程组是否是另外一个线程组的父线程组。
ThreadGroup的方法activeGroupCount( )计算它包含的线程组的数目,而activeCount()计算它包含的线程的数目。
线程可以用getThreadGroup()方法来访问它所在的线程组,但越界访问线程组之外的线程或线程组是不允许的。
线程组类还有几种重载形式的enumerate( )方法,用以把组内活动线程组或线程的引用复制进指定数组,返回值为活动线程组或线程的数目。例如,在例9-15中,若用:
int ct=grp1.enumerate(threadList);
其中,threadList是线程数组,则会把Thread-1的引用复制进threadList,ct则被赋值为1。
getMaxPriority( )方法用于获取线程组的最大优先级。setMaxPriority( )方法则用于设置线程组的最大优先级。
对于setMaxPriority( )方法,它对于线程组中优先级大于参数pri的线程没有作用。这个方法首先调用checkAccess( )方法判断这个线程组的线程是否允许被更改,如果不允许,则会抛出一个异常。然后判断setMaxPriority( )方法的参数pri是否在Thread.MIN_PRIORITY和Thread.MAX_PRIORITY之间,如果不是,则什么都不改变,否则这个线程组的对象的优先级将被设成参数pri和该线程组的父线程组所允许的最大优先级中较小的一个。对于没有父进程组的系统进程组,它们的线程优先级将被直接设置成pri。这个过程将对线程组的所有子线程组递归调用。