Check out the new USENIX Web site.


using java

Thread Groups


by Prithvi Rao

Prithvi Rao is the co-founder of KiwiLabs, which specializes in soft-ware engineering methodology and Java/ CORBA training. He has also worked on the de-velopment of the MACH OS and a real-time version of MACH. He is an adjunct faculty at Carnegie Mellon and teaches in the Heinz School of Public Policy and Management.


In a previous article I introduced the use of threads within Java. It is necessary to have a deeper understanding of this topic in order to write serious Java applications. This article presents an introduction to the ThreadGroup class. This class serves to organize threads. In a limited sense, it is analogous to the concept of databases, in which semantically similar pieces of information are grouped in a common repository. Naturally, another such example is a directory (analogous to the thread group) and an individual file (a thread).

Thread Group Characteristics
A thread group can contain a group of threads or can contain other thread groups that contain threads (similar to directories, which contain subdirectories, which contain files). The resulting thread group hierarchy is a tree structure.

One of the key characteristics of thread groups is that it is possible to affect the state of all threads in the hierarchy with a single call. For instance, it is possible to stop every thread in the thread group with a single call. This is one reason to use thread groups.

Consider the following code:

   ThreadGroup A = new ThreadGroup("A");
   ThreadGroup B = new ThreadGroup(A, "B");
   ThreadGroup C = new ThreadGroup(A, "C");
   ThreadGroup D = new ThreadGroup(C, "D");
   ThreadGroup E = new ThreadGroup("E");

The resulting hierarchy is as follows:

From main, A and E are descendants. ThreadGroup B is a descendant of A, and so is ThreadGroup C. Finally, ThreadGroup D is a descendant of C.

One way to improve the performance of an application utilizing thread groups is to use a single call to affect the state of threads in a hierarchy. If this were not possible, then each branch of the tree would have to be traversed, and this could be a time-consuming operation.

Another application for thread groups is in a multiprocessing environment. A given CPU may run threads that are in a given group (this needs operating-system support, however), and it is possible to assign different priorities to the different thread groups depending on the application. Generally, however, this requires significant operating-system support not usually found on nonrealtime systems.

Although at the time of this writing there are not too many examples of JVMs that have been ported to running on realtime operating systems, this is likely to happen, given that modern-day audio and video applications must meet strict time deadlines.

The main Thread Group
Thread groups are organized by name. Each thread group must have a unique name. All ThreadGroup constructors take a name as an argument. The default thread group in a Java application is main.

When you start an application, the thread group is main. Unless otherwise specified, all threads will be created as part of the main thread group. If you run applets within a browser, the name of the root thread group will depend on the browser.

Creating a new thread without specifying a thread group in the thread's constructor places the thread in the same thread group as the creator.

Thread Constructors
The following four thread constructors create the thread in the current thread group.

   Thread(Runnable, String)

The following three thread constructors create the thread in a specific thread group.

   Thread(ThreadGroup, Runnable)
   Thread(ThreadGroup, String)
   Thread(ThreadGroup, Runnable, String)

It is possible to learn to which thread group a particular thread belongs by using the getThreadGroup() method.

   ThreadGroup Z = foo.getThreadGroup();

It is also possible to enumerate threads within a particular thread group. Consider the following:

   ThreadGroup X = Thread.currentThread().getThreadGroup();
   int numThreads = X.getActiveThreads();
   Thread threads[] = new Thread[numThreads];
   for (int n = 0; n < numThreads; i++)

Limiting Priorities
It is possible to limit the priority of any thread that is inserted into a thread group. The call to setMaxPriority will enforce an upper limit of the thread group as a whole. The following example demonstrates the use of setMaxPriority:

   ThreadGroup X = new ThreadGroup("BackGround");
   Thread Y = new Thread(X, this);
   Thread Z = new Thread(X, this);

The threads Y and Z are usually created with the default priority of NORMAL_PRIORITY, which is equal to 5. In the example above, before Z was created the maximum priority was set to MIN_PRIORITY+2, which is now 3. So the creation of Z results in its priority value of 3.

Thread Groups and Priorities
All thread groups that are descendants of ThreadGroup will be affected by a call to setMaxPriority.

Attempting to set the priority of a thread higher than the priority of the thread group to which it belongs will result in SecurityException being thrown.

Once the maximum priority of a thread group has been lowered, it cannot be raised.

The maximum priority of the system ThreadGroup is MAX_PRIORITY (10). The maximum priority of the applet ThreadGroup is 6.

Thread Groups and Security
The Thread and ThreadGroup classes both have a method called checkAccess(), and they both call the Security Manager's checkAccess() method. The Security Manager checks to see whether the threads are permitted to gain access to certain operations. If there is a violation, the Security Manager throws an exception (SecurityException), otherwise returns. Consider the following:

   void checkAccess(Thread t)
checks if the current thread is allowed to modify the state of the thread.

   void checkAccess(ThreadGroup g)
checks if the current ThreadGroup is allowed to modify the state of the ThreadGroup g.

Most of the methods in the Thread class and the ThreadGroup class call SecurityManager before performing the actual operation. For example, the implementation of stop() reveals the following:

   public void stop()
   SecurityManager V = System.getSecurityManager();
   if (V != null)
   stop(new ThreadDeath());

The Java Security Manager implements security on an "all-or-none" basis. In other words, there is no granularity to distinguish allowable operations. If the Security Manager does not permit a thread to suspend another thread, then it also does not allow the same thread to set the other thread's daemon status. The setDaemon() function changes the daemon status of the thread group. It does not alter the daemon status of any individual threads within the group. If a group is made a daemon group, then it will be destroyed automatically when all of the threads it contains are terminated.

The following is a list of ThreadGroup methods that call ThreadGroup's checkAccess() method:

   ThreadGroup(ThreadGroup parent, String name)

The following Thread methods call checkAccess():


Recall that a standalone Java application has no security manager, so threads can modify and inspect any other thread. Within an applet, a thread can manipulate another thread only if both threads are members of the applet's ThreadGroup. A thread cannot manipulate a thread that is in another applet.

We have presented the use of thread groups and their interaction with the Security Manager in Java. The use of thread groups is critical to writing advanced applications in Java. However, the programmer must be aware of the differences in their interaction with the Security Manager depending on whether the threads run in a standalone program or as applets.

In future articles I will present applications using ThreadGroup to further demonstrate the use of this important Java class.


?Need help? Use our Contacts page.
Last changed: 18 Apr. 2000 mc
Using Java index
;login: index