Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

Java 101: Understanding Java threads, Part 1: Introducing threads and runnables

Learn how to improve Java application performance using Java threads

  • Print
  • Feedback

Page 4 of 5

Sleeping threads don't lie

Thread also supplies a sleep(long millis, int nanos) method, which puts the thread to sleep for millis milliseconds and nanos nanoseconds. Because most JVM-based platforms do not support resolutions as small as a nanosecond, JVM thread-handling code rounds the number of nanoseconds to the nearest number of milliseconds. If a platform does not support a resolution as small as a millisecond, JVM thread-handling code rounds the number of milliseconds to the nearest multiple of the smallest resolution that the platform supports.

Is it dead or alive?

When a program calls Thread's start() method, a time period (for initialization) passes before a new thread calls run(). After run() returns, a time period passes before the JVM cleans up the thread. The JVM considers the thread to be alive immediately prior to the thread's call to run(), during the thread's execution of run(), and immediately after run() returns. During that interval, Thread's isAlive() method returns a Boolean true value. Otherwise, that method returns false.

isAlive() proves helpful in situations where a thread needs to wait for another thread to finish its run() method before the first thread can examine the other thread's results. Essentially, the thread that needs to wait enters a while loop. While isAlive() returns true for the other thread, the waiting thread calls sleep(long millis) (or sleep(long millis, int nanos)) to periodically sleep (and avoid wasting many CPU cycles). Once isAlive() returns false, the waiting thread can examine the other thread's results.

Where would you use such a technique? For starters, how about a modified version of CalcPI1, where the starting thread waits for the new thread to finish before printing pi's value? Listing 4's CalcPI2 source code demonstrates that technique:

Listing 4. CalcPI2.java

// CalcPI2.java
class CalcPI2
{
   public static void main (String [] args)
   {
      MyThread mt = new MyThread ();
      mt.start ();
      while (mt.isAlive ())
        try
        {
            Thread.sleep (10); // Sleep for 10 milliseconds
        }
        catch (InterruptedException e)
        {
        }
      System.out.println ("pi = " + mt.pi);
   }
}
class MyThread extends Thread
{
   boolean negative = true;
   double pi; // Initializes to 0.0, by default
   public void run ()
   {
      for (int i = 3; i < 100000; i += 2)
      {
           if (negative)
               pi -= (1.0 / i);
           else
               pi += (1.0 / i);
           negative = !negative;
      }
      pi += 1.0;
      pi *= 4.0;
      System.out.println ("Finished calculating PI");
   }
}

CalcPI2's starting thread sleeps in 10 millisecond intervals, until mt.isAlive () returns false. When that happens, the starting thread exits from its while loop and prints pi's contents. If you run this program, you will see output similar (but probably not identical) to the following:

Finished calculating PI
pi = 3.1415726535897894

Now doesn't that look more accurate?

Is it alive?

A thread could possibly call the isAlive() method on itself. However, that does not make sense because isAlive() will always return true.

Joining forces

Because the while loop/isAlive() method/sleep() method technique proves useful, Sun packaged it into a trio of methods: join(), join(long millis), and join(long millis, int nanos). The current thread calls join(), via another thread's thread object reference when it wants to wait for that other thread to terminate. In contrast, the current thread calls join(long millis) or join(long millis, int nanos) when it wants to either wait for that other thread to terminate or wait until a combination of millis millseconds and nanos nanoseconds passes. (As with the sleep() methods, the JVM thread-handling code will round up the argument values of the join(long millis) and join(long millis, int nanos) methods.) Listing 5's CalcPI3 source code demonstrates a call to join():

Listing 5. CalcPI3.java

// CalcPI3.java
class CalcPI3
{
   public static void main (String [] args)
   {
      MyThread mt = new MyThread ();
      mt.start ();
      try
      {
          mt.join ();
      }
      catch (InterruptedException e)
      {
      }
      System.out.println ("pi = " + mt.pi);
   }
}
class MyThread extends Thread
{
   boolean negative = true;
   double pi; // Initializes to 0.0, by default
   public void run ()
   {
      for (int i = 3; i < 100000; i += 2)
      {
           if (negative)
               pi -= (1.0 / i);
           else
               pi += (1.0 / i);
           negative = !negative;
      }
      pi += 1.0;
      pi *= 4.0;
      System.out.println ("Finished calculating PI");
   }
}

CalcPI3's starting thread waits for the thread that associates with the MyThread object, referenced by mt, to terminate. The starting thread then prints pi's value, which is identical to the value that CalcPI2 outputs.

Do not attempt to join the current thread to itself because the current thread will wait forever.)

Census taking

In some situations, you might want to know which threads are actively running in your program. Thread supplies a pair of methods to help you with that task: activeCount() and enumerate(Thread [] thdarray). But those methods work only in the context of the current thread's thread group. In other words, those methods identify only active threads that belong to the same thread group as the current thread. (I discuss the thread group—an organizational mechanism—concept in a future series article.)

The static activeCount() method returns a count of the threads actively executing in the current thread's thread group. A program uses this method's integer return value to size an array of Thread references. To retrieve those references, the program must call the static enumerate(Thread [] thdarray) method. That method's integer return value identifies the total number of Thread references that enumerate(Thread []thdarray) stores in the array. To see how these methods work together, check out Listing 6:

Listing 6. Census.java

// Census.java
class Census
{
   public static void main (String [] args)
   {
      Thread [] threads = new Thread [Thread.activeCount ()];
      int n = Thread.enumerate (threads);
      for (int i = 0; i < n; i++)
           System.out.println (threads [i].toString ());
   }
}

When run, this program produces output similar to the following:

Thread[main,5,main]

The output shows that one thread, the starting thread, is running. The leftmost main identifies that thread's name. The 5 indicates that thread's priority, and the rightmost main identifies that thread's thread group. You might be disappointed that you cannot see any system threads, such as the garbage collector thread, in the output. That limitation results from Thread's enumerate(Thread [] thdarray) method, which interrogates only the current thread's thread group for active threads. However, the ThreadGroup class contains multiple enumerate() methods that allow you to capture references to all active threads, regardless of thread group. Later in this series, I will show you how to enumerate all references when I explore ThreadGroup.

activeCount() and NullPointerException

Do not depend on activeCount()'s return value when iterating over an array. If you do, your program runs the risk of throwing NullPointerException objects. Why? Between the calls to activeCount() and enumerate(Thread [] thdarray), one or more threads might possibly terminate. As a result, enumerate(Thread [] thdarray) would copy fewer thread references into its array. Therefore, think of activeCount()'s return value as a maximum value for array-sizing purposes only. Also, think of enumerate(Thread [] thdarray)'s return value as representing the number of active threads at the time of a program's call to that method.

  • Print
  • Feedback

Resources
  • Learn more about Java: See the complete listing for Jeff Friesen's Java 101 series -- archived on JavaWorld.
  • Also see the Java Tips series: More than five years of compiled tips from JavaWorld's expert readers.