The ThreadGroup class allows us
to deal with threads wholesale: we can use it to arrange threads in groups
and deal with the groups as a whole. A thread group can contain other
thread groups in addition to individual threads, so our arrangements can
be hierarchical. Thread groups are particularly useful when we want to
start a task that might create many threads of its own. By assigning the
task a thread group, we can later identify and control all the task’s
threads. Thread groups are also the subject of restrictions that can be
imposed by the Java Security Manager, so we can restrict a thread’s
behavior according to its thread group. For example, we can forbid threads
in a particular group from interacting with threads in other groups. This
is one way web browsers can prevent threads started by Java applets from
stopping important system threads.
When we create a thread, it normally becomes part of the thread group to which the currently running thread belongs. To create a new thread group of our own, we can call the constructor:
ThreadGroupmyTaskGroup=newThreadGroup("My Task Group");
The ThreadGroup constructor takes
a name, which a debugger can use to help you identify the group. (You can
also assign names to the threads themselves.) Once we have a group, we can
put threads in the group by supplying the ThreadGroup object as an argument to the
Thread constructor:
ThreadmyTask=newThread(myTaskGroup,taskPerformer);
Here, myTaskGroup is the thread
group, and taskPerformer is the target
object (the Runnable object that
performs the task). Any additional threads that myTask creates also belong to the myTaskGroup thread group.
The ThreadGroup class
exists so that you can control threads in batches. It has methods that
parallel the basic Thread control
methods—even the deprecated stop(), suspend(), and
resume(). These methods
operate on all the threads in a thread group. You can also mark a thread
group as a “daemon”; a daemon thread group is automatically removed when
all of its children are gone. If a thread group isn’t a daemon, you have
to call destroy() in order to
remove it when it is empty.
We can set the maximum priority for threads created in a thread
group by calling setMaximumPriority().
Thereafter, no threads can be created in the thread group with a
priority to be higher than the maximum; threads that change their
priority can’t set their new priority to be higher than the
maximum.
Finally, you can get a list of all threads in a group. The method
activeCount() tells you
how many threads are in the group; the method enumerate() gives you a
list of them. We used the enumerate()
method earlier when we showed the state of all threads in the default
thread group using the static Thread.enumerate()method. The argument to
enumerate() is an array of Threads that enumerate() fills in with the group’s threads.
Both activeCount() and enumerate() operate recursively on all thread
groups that are contained in the group.
In Java, unchecked exceptions that are not caught by any
method eventually bubble up to the run() method of the running thread and are
thrown from there. By default, Java deals with these by simply printing
them to the system error stream or log and terminating the thread.
However, you can specify your own “catchall” behavior for these
exceptions by subclassing ThreadGroup and
overriding the uncaughtException()
method. When an uncaught exception is generated, it is handed to this
method, which can take some action or throw it again before the thread
terminates.
In Java 5.0, this pattern was extended by defining an interface,
Thread.UncaughtExceptionHandler, and adding
both per-thread and systemwide uncaught exception handlers in addition
to the per-ThreadGroup exception
handler. We can handle uncaught exceptions for a single thread like
this:
Threadthread=newThread();thread.setUncaughtExceptionHandler(newThread.UncaughtExceptionHandler(){publicvoiduncaughtException(Threadt,Throwablee){System.err.println(t+" threw exception: "+e);}});
This example prints the exception before the thread dies. We could
have set the same handler on the ThreadGroup in the same way or assigned it for
all exceptions using the static Thread.setDefaultUncaughtExceptionHandler()
method.