This is explained in Effective Java 2nd Ed., Item 73.
Thread groups were originally envisioned as a mechanism
for isolating applets for security purposes. They never really fulfilled this
promise, and their security importance has waned to the extent that they aren’t
even mentioned in the standard work on the Java security model [Gong03].
[...] In an ironic twist, the ThreadGroup
API is weak from a thread safety
standpoint. To get a list of the active threads in a thread group, you must invoke
the enumerate
method, which takes as a parameter an array large enough to hold
all the active threads. The activeCount
method returns the number of active
threads in a thread group, but there is no guarantee that this count will still be
accurate once an array has been allocated and passed to the enumerate
method. If
the thread count has increased and the array is too small, the enumerate
method
silently ignores any threads for which there is no room in the array.
The API that lists the subgroups of a thread group is similarly flawed. While
these problems could have been fixed with the addition of new methods, they
haven’t, because there is no real need: thread groups are obsolete.
Prior to release 1.5, there was one small piece of functionality that was available
only with the ThreadGroup
API: the ThreadGroup.uncaughtException
method was the only way to gain control when a thread threw an uncaught exception.
This functionality is useful, for example, to direct stack traces to an application-
specific log. As of release 1.5, however, the same functionality is available
with Thread
’s setUncaughtExceptionHandler
method.
To summarize, thread groups don’t provide much in the way of useful functionality,
and much of the functionality they do provide is flawed. Thread groups
are best viewed as an unsuccessful experiment, and you should simply ignore their
existence. If you design a class that deals with logical groups of threads, you
should probably use thread pool executors (Item 68).