Thread groups
Thread groups
All threads belong to a thread group. This can be
either the default thread group or a group you explicitly specify when you
create the thread. At creation, the thread is bound to a group and cannot change
to a different group. Each application has at least one thread that belongs to
the system thread group. If you create more threads without specifying a group,
they will also belong to the system thread group.
Thread groups must also belong to other thread groups.
The thread group that a new one belongs to must be specified in the constructor.
If you create a thread group without specifying a thread group for it to belong
to, it will be placed under the system thread group. Thus, all thread groups in
your application will ultimately have the system thread group as the parent.
The reason for the existence of thread groups is hard
to determine from the literature, which tends to be confusing on this subject.
It’s often cited as “security reasons.” According to Arnold
& Gosling, “Threads within a thread group
can modify the other threads in the group, including any farther down the
hierarchy. A thread cannot modify threads outside of its own group or contained
groups.” It’s hard to know what “modify” is supposed to
mean here. The following example shows a thread in a “leaf” subgroup
modifying the priorities of all the threads in its tree of thread groups as well
as calling a method for all the threads in its tree.
//:
c14:TestAccess.java
//
How threads can access other threads
//
in a parent thread group.
public
class
TestAccess {
public
static
void
main(String[] args) {
ThreadGroup
x =
new
ThreadGroup("x"),
y =
new
ThreadGroup(x,
"y"),
z =
new
ThreadGroup(y,
"z");
Thread
one =
new
TestThread1(x,
"one"),
two =
new
TestThread2(z,
"two");
}
}
class
TestThread1
extends
Thread {
private
int
i;
TestThread1(ThreadGroup g, String name) {
super(g,
name);
}
void
f() {
i++; // modify this
thread
System.out.println(getName() + "
f()");
}
}
class
TestThread2
extends
TestThread1 {
TestThread2(ThreadGroup g, String name) {
super(g,
name);
start();
}
public
void
run() {
ThreadGroup g =
getThreadGroup().getParent().getParent();
g.list();
Thread[] gAll =
new
Thread[g.activeCount()];
g.enumerate(gAll);
for(int
i = 0; i < gAll.length; i++) {
gAll[i].setPriority(Thread.MIN_PRIORITY);
((TestThread1)gAll[i]).f();
}
g.list();
}
}
///:~
In main( ), several ThreadGroups
are created, leafing off from each other: x has no argument but its name
(a String), so it is automatically placed in the “system”
thread group, while y is under x and z is under y.
Note that initialization happens in textual order so this code is legal.
Two threads are created and placed in different thread
groups. TestThread1 doesn’t have a run( ) method but it
does have an f( ) that modifies the thread and prints something so
you can see it was called. TestThread2 is a subclass of
TestThread1 and its run( ) is fairly elaborate. It first gets
the thread group of the current thread, then moves up the heritage tree by two
levels using getParent( ). (This is contrived since I purposely
place the TestThread2 object two levels down in the hierarchy.) At this
point, an array of references to Threads is created using the method
activeCount( ) to ask how many threads are in this thread group and
all the child thread groups. The enumerate( ) method places
references to all of these threads in the array gAll, then I simply move
through the entire array calling the f( ) method for each thread, as
well as modifying the priority. Thus, a thread in a “leaf” thread
group modifies threads in parent thread groups.
The debugging method list( ) prints all
the information about a thread group to standard output and is helpful when
investigating thread group behavior. Here’s the output of the
program:
java.lang.ThreadGroup[name=x,maxpri=10]
Thread[one,5,x]
java.lang.ThreadGroup[name=y,maxpri=10]
java.lang.ThreadGroup[name=z,maxpri=10]
Thread[two,5,z]
one
f()
two
f()
java.lang.ThreadGroup[name=x,maxpri=10]
Thread[one,1,x]
java.lang.ThreadGroup[name=y,maxpri=10]
java.lang.ThreadGroup[name=z,maxpri=10]
Thread[two,1,z]
Not only does list( ) print the class name
of ThreadGroup or Thread, but it also prints the thread group name
and its maximum priority. For threads, the thread name is printed, followed by
the thread priority and the group that it belongs to. Note that
list( ) indents the threads and thread groups to indicate that they
are children of the unindented thread group.
You can see that f( ) is called by the
TestThread2 run( ) method, so it’s obvious that all
threads in a group are vulnerable. However, you can access only the threads that
branch off from your own system thread group tree, and perhaps this is
what is meant by “safety.” You cannot access anyone else’s
system thread group tree.
Controlling thread groups
Putting aside the safety issue, one thing thread
groups seem to be useful for is control: you can perform certain operations on
an entire thread group with a single command. The following example demonstrates
this, and the restrictions on priorities within thread groups. The commented
numbers in parentheses provide a reference to compare to the
output.
//:
c14:ThreadGroup1.java
//
How thread groups control priorities
//
of the threads inside them.
public
class
ThreadGroup1 {
public
static
void
main(String[] args) {
// Get the system thread &
print its Info:
ThreadGroup sys =
Thread.currentThread().getThreadGroup();
sys.list(); //
(1)
// Reduce the system thread group
priority:
sys.setMaxPriority(Thread.MAX_PRIORITY - 1);
// Increase the main thread
priority:
Thread curr = Thread.currentThread();
curr.setPriority(curr.getPriority() + 1);
sys.list(); //
(2)
// Attempt to set a new group to
the max:
ThreadGroup g1 =
new
ThreadGroup("g1");
g1.setMaxPriority(Thread.MAX_PRIORITY);
// Attempt to set a new thread to
the max:
Thread t =
new
Thread(g1,
"A");
t.setPriority(Thread.MAX_PRIORITY);
g1.list(); //
(3)
// Reduce g1's max priority, then
attempt
// to increase
it:
g1.setMaxPriority(Thread.MAX_PRIORITY - 2);
g1.setMaxPriority(Thread.MAX_PRIORITY);
g1.list(); //
(4)
// Attempt to set a new thread to
the max:
t =
new
Thread(g1,
"B");
t.setPriority(Thread.MAX_PRIORITY);
g1.list(); //
(5)
// Lower the max priority below
the default
// thread
priority:
g1.setMaxPriority(Thread.MIN_PRIORITY + 2);
// Look at a new thread's priority
before
// and after changing
it:
t =
new
Thread(g1,
"C");
g1.list(); //
(6)
t.setPriority(t.getPriority() -1);
g1.list(); //
(7)
// Make g2 a child Threadgroup of
g1 and
// try to increase its
priority:
ThreadGroup g2 =
new
ThreadGroup(g1,
"g2");
g2.list(); //
(8)
g2.setMaxPriority(Thread.MAX_PRIORITY);
g2.list(); //
(9)
// Add a bunch of new threads to
g2:
for
(int
i = 0; i < 5; i++)
new
Thread(g2, Integer.toString(i));
// Show information about all
threadgroups
// and
threads:
sys.list(); //
(10)
System.out.println("Starting all
threads:");
Thread[] all =
new
Thread[sys.activeCount()];
sys.enumerate(all);
for(int
i = 0; i < all.length; i++)
if(!all[i].isAlive())
all[i].start();
// Suspends & Stops all
threads in
// this group and its
subgroups:
System.out.println("All threads
started");
sys.suspend(); // Deprecated in
Java 2
// Never gets
here...
System.out.println("All threads
suspended");
sys.stop(); // Deprecated in Java
2
System.out.println("All threads
stopped");
}
}
///:~
The output that follows has been edited to allow it to
fit on the page (the java.lang. has been removed) and to add numbers to
correspond to the commented numbers in the listing above.
(1)
ThreadGroup[name=system,maxpri=10]
Thread[main,5,system]
(2)
ThreadGroup[name=system,maxpri=9]
Thread[main,6,system]
(3)
ThreadGroup[name=g1,maxpri=9]
Thread[A,9,g1]
(4)
ThreadGroup[name=g1,maxpri=8]
Thread[A,9,g1]
(5)
ThreadGroup[name=g1,maxpri=8]
Thread[A,9,g1]
Thread[B,8,g1]
(6)
ThreadGroup[name=g1,maxpri=3]
Thread[A,9,g1]
Thread[B,8,g1]
Thread[C,6,g1]
(7)
ThreadGroup[name=g1,maxpri=3]
Thread[A,9,g1]
Thread[B,8,g1]
Thread[C,3,g1]
(8)
ThreadGroup[name=g2,maxpri=3]
(9)
ThreadGroup[name=g2,maxpri=3]
(10)ThreadGroup[name=system,maxpri=9]
Thread[main,6,system]
ThreadGroup[name=g1,maxpri=3]
Thread[A,9,g1]
Thread[B,8,g1]
Thread[C,3,g1]
ThreadGroup[name=g2,maxpri=3]
Thread[0,6,g2]
Thread[1,6,g2]
Thread[2,6,g2]
Thread[3,6,g2]
Thread[4,6,g2]
Starting
all threads:
All
threads started
All programs have at least one thread running, and the
first action in main( ) is to call the static method of
Thread called currentThread( ). From this thread, the thread
group is produced and list( ) is called for the result. The output
is:
(1)
ThreadGroup[name=system,maxpri=10]
Thread[main,5,system]
You can see that the name of the main thread group is
system, and the name of the main thread is main, and it belongs to
the system thread group.
The second exercise shows that the system
group’s maximum priority can be reduced and the main thread can
have its priority increased:
(2)
ThreadGroup[name=system,maxpri=9]
Thread[main,6,system]
The third exercise creates a new thread group,
g1, which automatically belongs to the system thread group since
it isn’t otherwise specified. A new thread A is placed in
g1. After attempting to set this group’s maximum priority to the
highest level and A’s priority to the highest level, the result
is:
(3)
ThreadGroup[name=g1,maxpri=9]
Thread[A,9,g1]
Thus, it’s not possible to change the thread
group’s maximum priority to be higher than its parent thread group.
The fourth exercise reduces g1’s maximum
priority by two and then tries to increase it up to Thread.MAX_PRIORITY.
The result is:
(4)
ThreadGroup[name=g1,maxpri=8]
Thread[A,9,g1]
You can see that the increase in maximum priority
didn’t work. You can only decrease a thread group’s maximum
priority, not increase it. Also, notice that thread A’s priority
didn’t change, and now it is higher than the thread group’s maximum
priority. Changing a thread group’s maximum priority doesn’t affect
existing threads.
The fifth exercise attempts to set a new thread to
maximum priority:
(5)
ThreadGroup[name=g1,maxpri=8]
Thread[A,9,g1]
Thread[B,8,g1]
The new thread cannot be changed to anything higher
than the maximum thread group priority.
The default thread priority for this program is six;
that’s the priority a new thread will be created at and where it will stay
if you don’t manipulate the priority. Exercise 6 lowers the maximum thread
group priority below the default thread priority to see what happens when you
create a new thread under this condition:
(6)
ThreadGroup[name=g1,maxpri=3]
Thread[A,9,g1]
Thread[B,8,g1]
Thread[C,6,g1]
Even though the maximum priority of the thread group
is three, the new thread is still created using the default priority of six.
Thus, maximum thread group priority does not affect default priority. (In fact,
there appears to be no way to set the default priority for new threads.)
After changing the priority, attempting to decrement
it by one, the result is:
(7)
ThreadGroup[name=g1,maxpri=3]
Thread[A,9,g1]
Thread[B,8,g1]
Thread[C,3,g1]
Only when you attempt to change the priority is the
thread group’s maximum priority enforced.
A similar experiment is performed in (8) and (9), in
which a new thread group g2 is created as a child of g1 and its
maximum priority is changed. You can see that it’s impossible for
g2’s maximum to go higher than g1’s:
(8)
ThreadGroup[name=g2,maxpri=3]
(9)
ThreadGroup[name=g2,maxpri=3]
Also notice that g2 is automatically set to the
thread group maximum priority of g1 as g2 is created.
After all of these experiments, the entire system of
thread groups and threads is printed:
(10)ThreadGroup[name=system,maxpri=9]
Thread[main,6,system]
ThreadGroup[name=g1,maxpri=3]
Thread[A,9,g1]
Thread[B,8,g1]
Thread[C,3,g1]
ThreadGroup[name=g2,maxpri=3]
Thread[0,6,g2]
Thread[1,6,g2]
Thread[2,6,g2]
Thread[3,6,g2]
Thread[4,6,g2]
So because of the rules of thread groups, a child
group must always have a maximum priority that’s less than or equal to its
parent’s maximum priority.
The last part of this program demonstrates methods for
an entire group of threads. First the program moves through the entire tree of
threads and starts each one that hasn’t been started. For drama, the
system group is then suspended and finally stopped. (Although it’s
interesting to see that suspend( ) and stop( ) work on
entire thread groups, you should keep in mind that these methods are deprecated
in Java 2.) But when you suspend the system group you also suspend the
main thread and the whole program shuts down, so it never gets to the
point where the threads are stopped. Actually, if you do stop the main
thread it throws a ThreadDeath exception, so this is not a typical thing
to do. Since ThreadGroup is inherited from Object, which contains
the wait( ) method, you can also choose to suspend the program for
any number of seconds by calling wait(seconds * 1000). This must acquire
the lock inside a synchronized block, of course.
The ThreadGroup class also has
suspend( ) and resume( ) methods so you can stop and
start an entire thread group and all of its threads and subgroups with a single
command. (Again, suspend( ) and resume( ) are deprecated
in Java 2.)
Thread groups can seem a bit mysterious at first, but
keep in mind that you probably won’t be using them directly very
often.
|