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 5 of 5

Antibugging

If your program malfunctions, and you suspect that the problem lies with a thread, you can learn details about that thread by calling Thread's dumpStack() and toString() methods. The static dumpStack() method, which provides a wrapper around new Exception ("Stack trace").printStackTrace ();, prints a stack trace for the current thread. toString() returns a String object that describes the thread's name, priority, and thread group according to the following format: Thread[thread-name,priority,thread-group]. (You will learn more about priority later in this series.)

The caste system

Not all threads are created equal. They divide into two categories: user and daemon. A user thread performs important work for the program's user, work that must finish before the application terminates. In contrast, a daemon thread performs housekeeping (such as garbage collection) and other background tasks that probably do not contribute to the application's main work but are necessary for the application to continue its main work. Unlike user threads, daemon threads do not need to finish before the application terminates. When an application's starting thread (which is a user thread) terminates, the JVM checks whether any other user threads are running. If some are, the JVM prevents the application from terminating. Otherwise, the JVM terminates the application regardless of whether daemon threads are running.

When to use currentThread()

In several places, this article refers to the concept of a current thread. If you need access to a Thread object that describes the current thread, call Thread's static currentThread() method. Example: Thread current = Thread.currentThread ();.

When a thread calls a thread object's start() method, the newly started thread is a user thread. That is the default. To establish a thread as a daemon thread, the program must call Thread's setDaemon(boolean isDaemon) method with a Boolean true argument value prior to the call to start(). Later, you can check if a thread is daemon by calling Thread's isDaemon() method. That method returns a Boolean true value if the thread is daemon.

To let you play with user and daemon threads, I wrote UserDaemonThreadDemo:

Listing 7. UserDaemonThreadDemo.java

// UserDaemonThreadDemo.java
class UserDaemonThreadDemo
{
   public static void main (String [] args)
   {
      if (args.length == 0)
         new MyThread ().start ();
      else
      {
         MyThread mt = new MyThread ();
         mt.setDaemon (true);
         mt.start ();
      }
      try
      {
         Thread.sleep (100);
      }
      catch (InterruptedException e)
      {
      }
   }
}
class MyThread extends Thread
{
   public void run ()
   {
      System.out.println ("Daemon is " + isDaemon ());
      while (true);
   }
}

After compiling the code, run UserDaemonThreadDemo via the Java 2 SDK's java command. If you run the program with no command-line arguments, as in java UserDaemonThreadDemo, for example, new MyThread ().start (); executes. That code fragment starts a user thread that prints Daemon is false prior to entering an infinite loop. (You must press Ctrl-C or an equivalent keystroke combination to terminate that infinite loop.) Because the new thread is a user thread, the application keeps running after the starting thread terminates. However, if you specify at least one command-line argument, as in java UserDaemonThreadDemo x, for example, mt.setDaemon (true); executes, and the new thread will be a daemon. As a result, once the starting thread awakes from its 100-millisecond sleep and terminates, the new daemon thread will also terminate.

A setDaemon() exception

Note that the setDaemon(boolean isDaemon) method throws an IllegalThreadStateException object if a call is made to that method after the thread starts execution.

Runnables

After studying the previous section's examples, you might think that introducing multithreading into a class always requires you to extend Thread and have your subclass override Thread's run() method. That is not always an option, however. Java's enforcement of implementation inheritance prohibits a class from extending two or more superclasses. As a result, if a class extends a non-Thread class, that class cannot also extend Thread. Given that restriction, how is it possible to introduce multithreading into a class that already extends some other class? Fortunately, Java's designers realized that situations would arise where subclassing Thread wouldn't be possible. That realization led to the java.lang.Runnable interface and Thread constructors with Runnable parameters, such as Thread(Runnable target).

The Runnable interface declares a single method signature: void run();. That signature is identical to Thread's run() method signature and serves as a thread's entry of execution. Because Runnable is an interface, any class can implement that interface by attaching an implements clause to the class header and by providing an appropriate run() method. At execution time, program code can create an object, or runnable, from that class and pass the runnable's reference to an appropriate Thread constructor. The constructor stores that reference within the Thread object and ensures that a new thread calls the runnable's run() method after a call to the Thread object's start() method, which Listing 8 demonstrates:

Listing 8. RunnableDemo.java

// RunnableDemo.java

public class RunnableDemo extends java.applet.Applet implements Runnable
{
   private Thread t;

   public void run ()
   {
      while (t == Thread.currentThread ())
      {
           int width = rnd (30);
           if (width < 2)
               width += 2;

           int height = rnd (10);
           if (height < 2)
               height += 2;

           draw (width, height);
      }
   }

   public void start ()
   {
      if (t == null)
      {
         t = new Thread (this);
         t.start ();
      }
   }

   public void stop ()
   {
      if (t != null)
         t = null;
   }

   private void draw (int width, int height)
   {
      for (int c = 0; c < width; c++)
           System.out.print ('*');

      System.out.print ('\n');

      for (int r = 0; r < height - 2; r++)
      {
           System.out.print ('*');

           for (int c = 0; c < width - 2; c++)
                System.out.print (' ');

           System.out.print ('*');

           System.out.print ('\n');
      }

      for (int c = 0; c < width; c++)
           System.out.print ('*');

      System.out.print ('\n');
   }

   private int rnd (int limit)
   {
      // Return a random number x in the range 0 <= x < limit.

      return (int) (Math.random () * limit);
   }
}

RunnableDemo describes an applet for repeatedly outputting asterisk-based rectangle outlines on the standard output. To accomplish this task, Runnable must extend the java.applet.Applet class (java.applet identifies the package in which Applet is located -- I discuss packages in a future article) and implement the Runnable interface.

An applet provides a public void start() method, which is called (typically by a Web browser) when an applet is to start running, and provides a public void stop() method, which is called when an applet is to stop running.

The start() method is the perfect place to create and start a thread, and RunnableDemo accomplishes this task by executing t = new Thread (this); t.start ();. I pass this to Thread's constructor because the applet is a runnable due to RunnableDemo implementing Runnable.

The stop() method is the perfect place to stop a thread, by assigning null to the Thread variable. I cannot use Thread's public void stop() method for this task because this method has been deprecated -- it's unsafe to use.

The run() method contains an infinite loop that runs for as long as Thread.currentThread() returns the same Thread reference as located in Thread variable t. The reference in this variable is nullified when the applet's stop() method is called.

Because RunnableDemo's new output would prove too lengthy to include with this article, I suggest you compile and run that program yourself.

You will need to use the appletviewer tool and an HTML file to run the applet. Listing 9 presents a suitable HTML file -- the width and height are set to 0 because no graphical output is generated.

Listing 9. RunnableDemo.html

 <applet code="RunnableDemo" width="0" height="0"></applet>

Specify appletviewer RunnableDemo.html to run this applet.

Thread vs Runnable?

When you face a situation where a class can either extend Thread or implement Runnable, which approach do you choose? If the class already extends another class, you must implement Runnable. However, if that class extends no other class, think about the class name. That name will suggest that the class's objects are either active or passive. For example, the name Ticker suggests that its objects are active—they tick. Thus, the Ticker class would extend Thread, and Ticker objects would be specialized Thread objects. In contrast, Rectangle suggests passive objects—Rectangle objects do nothing on their own. Thus, the Rectangle class would implement Runnable, and Rectangle objects would use Thread objects (for testing or other purposes) instead of being specialized Thread objects.

Review

Users expect programs to achieve strong performance. One way to accomplish that task is to use threads. A thread is an independent path of execution through program code. Threads benefit GUI-based programs because they allow those programs to remain responsive to users while performing other tasks. In addition, threaded programs typically finish faster than their nonthreaded counterparts. This is especially true of threads running on a multiprocessor machine, where each thread has its own processor. The Thread and Thread subclass objects describe threads and associate with those entities. For those classes that cannot extend Thread, you must create a runnable to take advantage of multithreading.

Next month, I continue this series by showing you how to synchronize access to shared data.

About the author

Jeff Friesen has been involved with computers for the past 20 years. He holds a degree in computer science and has worked with many computer languages. Jeff has also taught introductory Java programming at the college level. In addition to writing for JavaWorld, he has written his own Java book for beginners? Java 2 by Example, Second Edition (Que Publishing, 2001; ISBN: 0789725932)?and helped write Using Java 2 Platform, Special Edition (Que Publishing, 2001; ISBN: 0789724685). Jeff goes by the nickname Java Jeff (or JavaJeff). To see what he's working on, check out his Website at http://www.javajeff.com.

Read more about Core Java in JavaWorld's Core Java section.

  • 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.