Grouping Threads
Every thread is a member of a thread group. A thread group provides a mechanism for collecting multiple threads into a single object and manipulating those threads all at once rather than individually. For example, you can interrupt all the threads within a group with a single method call. Thread groups are implemented by the ThreadGroup [1] class in the java.lang package.
[1] http://java.sun.com/j2se/1.3/docs/api/java/lang/ThreadGroup.html
The runtime system puts a thread into a thread group during thread construction. When you create a thread, you can either allow the runtime system to figure out the appropriate thread group, or you can explicitly set the new thread's group. The thread is a permanent member of whatever thread group it joins on its creation. You cannot move a thread to a new group after the thread has been created.
The Thread Group
If you create a new thread without specifying its group in the constructor, the runtime system automatically places the new thread in the same group as the thread that created it (called the current thread group and the current thread, respectively). When an application first starts up, the Java runtime environment creates a ThreadGroup named main. Thus, all new threads that a program creates become members of the main thread group unless the program explicitly creates other groups and puts threads in them.
Note
If you create a thread within an applet, the new thread's group may be something other than main, depending on the browser or the viewer in which the applet is running. Refer to the sections Threads in AWT Applets (page 449) and Threads in Swing Applets (page 459) in Chapter 11 for information.
Many programmers ignore thread groups altogether and allow the runtime system to handle all the details regarding thread groups. However, if your program creates a lot of threads that should be manipulated as a group or if you are implementing a custom security manager, you will likely want more control over thread groups. Continue reading for more details.
Creating a Thread Explicitly in a Group
To put a new thread in a thread group other than the default, you must specify the thread group explicitly when you create it. The Thread class has three constructors that set a new thread's group:
public Thread(ThreadGroup group, Runnable target) public Thread(ThreadGroup group, String name) public Thread(ThreadGroup group, Runnable target, String name)
Each constructor creates a new thread, initializes it based on the Runnable and String parameters, and makes the new thread a member of the specified group. For example, the following code sample creates a thread group (myThreadGroup) and then creates a thread (myThread) in that group:
ThreadGroup myThreadGroup = new ThreadGroup("My Group of Threads"); Thread myThread = new Thread(myThreadGroup, "a thread for my group");
The ThreadGroup passed into a Thread constructor can be any group: one created by your program, by the Java runtime environment, or by the browser in which an applet is running.
Getting a Thread's Group
To find out what group a thread is in, call its getThreadGroup method:
theGroup = myThread.getThreadGroup();
Once you've obtained a thread's ThreadGroup, you can query the group for information, such as what other threads are in the group. You also can modify the threads in that group by using a single method invocation.
Using the ThreadGroup Class
The ThreadGroup class manages groups of threads for programs. A ThreadGroup can contain any number of threads and can contain other ThreadGroups. The threads in a group are generally related, such as by who created them, what function they perform, or when they should be started and stopped.
The topmost thread group in an application is the thread group named main. A program can create threads and groups in the main group or in its subgroups. The result is a hierarchy of threads and groups, as shown in Figure 85.
Figure 85. Thread groups can be nested, thereby creating a hierarchy of groups and threads.
The ThreadGroup class has several categories of methods, discussed in the following sections.
- Collection Management Methods (page 304): Methods that manage the collection of threads and subgroups contained in the thread group
- Methods That Operate on the Group Object (page 304): Methods that set or get attributes of the ThreadGroup object
- Methods That Operate on All Threads within a Group (page 306): A set of methods that perform an operation on all the threads and subgroups within the group
- Access Restriction Methods (page 306): Methods that allow the security manager to restrict access to threads, based on group membership; allowed by ThreadGroup and Thread
Collection Management Methods
The ThreadGroup provides a set of methods that manage the threads and subgroups within the group and that allow other objects to query the ThreadGroup for information about its contents. For example, you can call ThreadGroup's activeCount method to learn the number of active threads currently in the group. This method can be used with the enumerate method to get an array filled with references to all the active threads in a ThreadGroup. For example, the listCurrentThreads method in the following example fills an array with all the active threads in the current thread group and prints their names:
public class EnumerateDemo { public void listCurrentThreads() { ThreadGroup currentGroup = Thread.currentThread().getThreadGroup(); int numThreads = currentGroup.activeCount(); Thread[] listOfThreads = new Thread[numThreads]; currentGroup.enumerate(listOfThreads); for (int i = 0; i < numThreads; i++) { System.out.println("Thread #" + i + " = " + listOfThreads[i].getName()); } } }
Other collection management methods provided by the ThreadGroup class include activeGroupCount, which returns an estimate of the number of active threads in the group, and list, which prints information about this thread group to the standard output.
Methods That Operate on the Group Object
The ThreadGroup class supports several attributes that are set and retrieved from the group as a whole. These attributes include the maximum priority that any thread within the group can have, whether the group is a daemon group, the name of the group, and the parent of the group.
The methods that get and set ThreadGroup attributes operate at the group level. They inspect or change the attribute on the ThreadGroup object, but they do not affect any of the threads within the group. Following are the ThreadGroup methods that operate at the group level:
- getMaxPriority and setMaxPriority
- getDaemon and setDaemon
- getName
- getParent and parentOf
- toString
When you use setMaxPriority to change a group's maximum priority, you are changing only the attribute on the group object; you are not changing the priority of any of the threads within it. Consider the following program that creates a group and a thread within that group:
public class MaxPriorityDemo { public static void main(String[] args) { ThreadGroup groupNORM = new ThreadGroup( "A group with normal priority"); Thread priorityMAX = new Thread(groupNORM, "A thread with maximum priority"); // set Thread's priority to max (10) priorityMAX.setPriority(Thread.MAX_PRIORITY); // set ThreadGroup's max priority to normal (5) groupNORM.setMaxPriority(Thread.NORM_PRIORITY); System.out.println("Group's maximum priority = " + groupNORM.getMaxPriority()); System.out.println("Thread's priority = " + priorityMAX.getPriority()); } }
When the ThreadGroup groupNORM is created, the thread inherits its maximum priority attribute from its parent thread group. In this case, the parent group priority is the maximum (MAX_PRIORITY) allowed by the Java runtime environment. Next, the program sets the priority of the priorityMAX thread to the maximum allowed by the Java runtime environment. Then the program lowers the group's maximum to the normal priority (NORM_PRIORITY). The setMaxPriority method does not affect the priority of the priorityMAX thread, so at this point, the priorityMAX thread has a priority of MAX_PRIORITY, which is greater than the maximum priority of its group. Here is the output from the program:
Group's maximum priority = 5 Thread's priority = 10
As you can see, a thread can have a priority higher than the maximum allowed by its group as long as the thread's priority is set before the group's maximum priority is lowered. A thread group's maximum priority is used to limit a thread's priority when the thread is first created within a group or when you use setPriority to change the thread's priority. Note that the setMaxPriority method does change the maximum priority of all its descendant-thread groups.
Similarly, a group's daemon status applies only to the group object. Changing a group's daemon status does not affect the daemon status of any thread in the group. Furthermore, a group's daemon status does not in any way determine the daemon status of its threadsyou can put any thread within a daemon thread group. The daemon status of a thread group simply indicates that the group will be destroyed when all its threads have been terminated.
Methods That Operate on All Threads within a Group
The ThreadGroup class has a method for interrupting all the threads within it: interrupt. The ThreadGroup's interrupt method calls the interrupt method on every thread in the group and its subgroups.
The resume, stop, and suspend methods operate on all threads in the group. However, these methods have been deprecated. Refer to Deprecated Thread Methods (page 527).
Access Restriction Methods
The ThreadGroup class itself does not impose any access restrictions, such as allowing threads from one group to inspect or to modify threads in a different group. Rather, the Thread and the ThreadGroup classes cooperate with security managers (subclasses of the java.lang.SecurityManager class), which can impose access restrictions based on thread group membership.
The Thread and the ThreadGroup classes both have a method, checkAccess, that calls the current security manager's checkAccess method. The security manager decides whether to allow the access based on the group membership of the threads involved. If access is not allowed, the checkAccess method throws a SecurityException. Otherwise, checkAccess simply returns.
Following is a list of ThreadGroup methods that call ThreadGroup's checkAccess method before performing the action of the method. These are called regulated accesses. A regulated access must be approved by the security manager before it can be completed.
- ThreadGroup(ThreadGroup parent , String name )
- setDaemon(boolean isDaemon )
- setMaxPriority(int maxPriority )
- destroy()
Here are the Thread class constructors and methods that call checkAccess before proceeding:
- Constructors that specify a thread group
- setPriority(int priority )
- setName(String name )
- setDaemon(boolean isDaemon )
By default, a standalone application does not have a security manager. No restrictions are imposed, and any thread can inspect or modify any other thread, regardless of the group in which they are located. You can define and implement your own access restrictions for thread groups by subclassing SecurityManager, overriding the appropriate methods, and installing the SecurityManager as the current security manager in your application.
The HotJava™ Web browser is an example of an application that implements its own security manager. HotJava needs to ensure that applets are well behaved and don't attempt any negative interactions with other applets, such as lowering the priority of another applet's threads, that are running at the same time. HotJava's security manager does not allow threads in different groups to modify one another. Note that access restrictions based on thread groups may vary among browsers, and thus applets may behave differently in different browsers.