JDK-4229558 : (thread) ThreadGroup.activeCount vs ThreadGroup.enumerate on 1.1 vs 1.2
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang
  • Affected Version: 1.2.0,1.3.0
  • Priority: P4
  • Status: Closed
  • Resolution: Not an Issue
  • OS: generic
  • CPU: generic
  • Submitted: 1999-04-14
  • Updated: 2005-11-03
  • Resolved: 2005-11-03
Related Reports
Duplicate :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Description
Name: vi73552			Date: 04/14/99


1. Exact steps ...

1) Create a thread group.
2) Add some threads to it.
3) Start some of them.
4) Get active Thread count.
5) Get enumeration of ThreadGroup.
6) Step through the enumeration and start the ones that return
   false for isAlive.

You can't do this on Java 1.2 because the enumeration only
returns the threads that return true for isAlive.

2. Java SOURCE CODE ...
ThreadGroup tg = new ThreadGroup("group1");
//*0
Thread[] t = new Thread[3];
t[0] = new Thread(tg,r[0],"thread"+0);
//*1
t[1] = new Thread(tg,r[1],"thread"+1); t[1].start();
//*2
t[2] = new Thread(tg,r[2],"thread"+2);
//*3

int count = tg.activeCount();
Thread[] a = new Thread[count];
int active = tg.enumerate(a);

for (int ndx = 0; ndx < active; ndx++)
    if (!active[ndx].isAlive())
        active[ndx].start();
    else
        System.out.println("Thread already alive: " + active[ndx]);

But this will only display that thread1 is already alive, and
thread0 and thread2 will never get started.

5. The out put of commands ...

java version "1.2"
Classic VM (build JDK-1.2-V, native threads)

java full version "JDK-1.2-V"
(Review ID: 56516) 
======================================================================

Name: boT120536			Date: 01/30/2001


java version "1.3.0"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.0-C)
Java HotSpot(TM) Client VM (build 1.3.0-C, mixed mode)


ThreadGroup tg=new ThreadGroup("unstoppable");
Thread t1=new Thread(tg,"thread1",runnable1);
Thread t2=new Thread(tg,"thread2",runnable2);

t1.start();

while (t1.isAlive()) try {Thread.sleep(1000);} catch (Throwable t) {;}

try {
  tg.stop();
} catch (IllegalThreadStateException itse) {
/* This is because t2 has not been started, and cannot be disposed() */
  System.err.println("Expected Exception occured:");
  itse.printStackTrace();
/* at this point you cannot enumerate which threads have not been started,
and you cannot start them, enumerate them, discover anything about them. */
} catch (Throwable t) {
  System.err.println("This was an unexpected error:");
  t.printStackTrace();
}

One must be able to guarantee the JVM will properly terminate without
requiring a call to System.exit(), or Runtime.exit().

In order to meet that requirement, all threads in existence must be daemon,
and the JVM will examine _ALL_ threads.

In Java, there is no reliable method to determine if System.exit() is
required.

There is no method available to see if there are any unstarted threads.

Since the mechanism to get a full thread dump under program control was
removed, this cannot be used to introspect the current state of the VM to
determine if there are threads which have not been started, and thus
determine if a System.exit() is required.

When bugs are fixed, it is the nature of the problem, which determines what
is implemented as a solution.

In this particular case, I am concerned that the nature of the problem
defined for Bug#4229558 will ignore the real problem.

Sincerely,

Ken Graham.
(Review ID: 115893)
======================================================================

Comments
EVALUATION From the description, relating to JLS section 12.8 about program exit: "One [an application designer] must be able to guarantee the JVM will properly terminate without requiring a call to System.exit(), or Runtime.exit(). In order to meet that requirement, all threads in existence must be daemon, and the JVM will examine _ALL_ threads." Let's distinguish between two meanings for the word thread. In the text above a "thread" is an independent thread of execution of bytecodes. But one can also think of a Thread class instance(object). For the purpose of explanation there can be upper case "Thread" to denote the Java class and in context, instances of that class, and "thread" to denote the entity that must terminate before a Java program (collection of threads) can exit (terminate/quit/stop). A Thread that has not had its start method invoked does not have a "thread" associated with it and cannot be required to exit. There cannot be thread death without thread life and this life cycle is independent of the life of a Thread instance. A program's termination cannot be prevented by an unaccounted for unstarted Thread object because only use of the Thread start method (or the JVM emulating its use) is sufficient to create the thread of bytecode execution associated with it in order for the Thread to be considered "alive" by the JVM. With Sun's Java SE implementation the JVM is almost completely unaware of the existence of a Thread instance until the start method of the instance is invoked (the reason it's "almost" unaware is not important). So although an alternative to ThreadGroup is required to track unstarted Thread instances for the reasons stated below, there is no hardship related to program termination being indeterminate due to a "lost" unstarted Thread. The following is to compensate for weak existing documentation as an FYI (but not as encouragement for ThreadGroup: see item 53 "Avoid thread groups" in Josh Bloch's book "Effective Java" for the one valid use for ThreadGroup and why it should otherwise be ignored). This is all in terms of current (version 5) Java SE. In the context of the ThreadGroup class an "active" thread is the same as a "live" thread in the sense of Thread.isAlive (i.e. "started but not dead/terminated"). Thread references are only in a ThreadGroup when there is a thread to go along with the Thread: that is, when the Thread is alive/active. When a Thread has its start method invoked the Thread reference is added to the designated ThreadGroup. When the thread terminates (returns from it's Thread.run method or is forcibly terminated) the corresponding Thread reference is removed from that same ThreadGroup instance. ThreadGroup.activeCount totals the active/alive threads of all Thread instances of all ThreadGroup instances sharing parentage ("subgroups") as well as the Thread instances of the given ThreadGroup. The same is true of Thread.activeCount, the javadoc of which should say it includes active/alive Thread instances in subgroups of the referenced Thread's group too. ThreadGroup.enumerate has a signature allowing both "this group only" and "this group and all its subgroups" handling via a "recurse" boolean argument. ThreadGroup.activeCount only covers the "this group and all its subgroups" case. But the enumerate method for filling a Thread array fills it with the Thread references contained in the ThreadGroup instance(s) which, by definition are active/alive. So keeping track of unstarted threads is simply not part of what ThreadGroup does, but its activeCount and enumerate methods are consistent with each other and Thread. What's left to do are some specific improvements to the documentation (RFE 4189292). For the text of the JLS reference above see the "View HTML" link here: http://java.sun.com/docs/books/jls/index.html (Sun's Java SE delays thread creation but it would probably be valid for an implementation to create the thread at Thread construction time if there was no visible difference in conflict with the Java spec. This probably means creating the thread in an initial suspended state so it cannot execute any bytecodes prior to start. So in the statement above that says "Thread references are only in a ThreadGroup when there is a thread to go along with the Thread" this implies "that is has executed at least one bytecode")
03-11-2005

WORK AROUND Name: vi73552 Date: 04/14/99 Don't use ThreadGroup.enumerate. At //*0 above, add Vector notStartedThreads = new Vector(); At //*1, //*2 and //*3 above, add notStartedThreads.addElement(t[x]); where x is 0, 1, and 2 Instead of the above for loop use: for (int ndx = 0; ndx < nonStaredThreads.size(); ndx++) { Thread ns = (Thread) nonStartedThreads.elementAt(ndx); if (!ns.isAlive() ns.start() else System.out.println("Thread already alive: " + ns); ======================================================================
11-06-2004