Home | Articles

Creating a Threaded Slide Show Applet

Water Lilies by Claude Monet 1897 A thread is a path of execution through a program. Single threaded programs have one path of execution, and multithreaded programs have two or more paths of execution. Single threaded programs can perform only one task at a time, and have to finish each task in sequence before they can start another. For most programs, one thread of execution is all you need, but sometimes it makes sense to use multiple threads in a program to accomplish multiple simultaneous tasks.

For example, a multithreaded math program can let the user set parameters for a new calculation while a previous calculation computes. Or a multithreaded word processor can let the user open a new document while a large file saves or spools to the printer. And sometimes, as you will see in this article, a multithreaded approach is necessary when you need a method to execute in a continuous loop in one thread and another thread to do something else like paint the display between loop iterations.

Claude Monet Self-Portrait 1886

This article describes multithreaded programming by presenting an example slide show applet. The images for the slide show applet are thumbnails of paintings by Claude Monet downloaded from a public web site. If Claude Monet were alive today, the thumbnails and slide show applet might very well be available from his own web site so prospective buyers can view his latest works and make electronic purchases.

All single or multithreaded programs execute in their own thread created and started by the underlying Java VM1. The Java VM also creates and starts other threads to help manage single or multithreaded program execution. For example, the Java VM starts the Finalizer thread to execute an object's finalize method before the object is garbage collected, and starts the AWT-EventQueue-0 thread to call an object's event handling methods such as actionPerformed and windowClosing. Because the Java VM spawns threads to handle the execution of a program, you might say that the Java VM itself is a multithreaded program.

Normally, you do not have to concern yourself with threads created by the Java VM to manage your program execution. As a developer, your primary concern is to determine whether your program works best single threaded or multithreaded, and then design and implement it accordingly. In making your decision, you should take into account that spawning additional threads carries overhead by consuming extra memory and processor resources. But sometimes a multithreaded approach is the best way to go so the user does not have to wait too long for a task to complete before starting another, or as in the case of the slide show applet, for the program to work at all.

Running the Slide Show Applet

Garden in Bordighera The slide show applet downloads three thumbnail paintings by Claude Monet and draws them one-by-one on the applet's panel. The thumbnail paintings are stored on a Web site and the slide show applet accesses them by their URL.

To run the slide show applet, get the SlideShow.java class file and the java.policy security policy file. Compile the SlideShow.java class file and create a slides.html file that looks like this:

  <HTML>
  <BODY>
    <APPLET CODE=SlideShow.class 
      WIDTH=250 
      HEIGHT=130>
    </APPLET>
  </BODY>
  </HTML>

Use the applet viewer tool to run the applet as follows:

appletviewer -J-Djava.security.policy=
java.policy slides.html

How the Threaded Applet Works

Rock Arch west
of Etretat The slide show applet executes in its own thread and spawns a second thread to retrieve images from an image array, retrieve captions from a text array, and call the repaint method to initiate drawing the image and caption text. The actual drawing of the image and text is handled by an event thread started by the Java VM.

Every thread has a name, so you can tell which thread is doing what by retrieving the current thread and getting its name with the getName method in the Thread class. When you create threads in your program, you can supply an optional name, and if you do not supply a name, the Java VM provides a default name that follows the following naming convention: Thread-0, Thread-1, and so on. Threads created by the Java VM have names too, as you will see in the sample run below.

Slide Show Sample Run

Bulb field
with windmill near Leyden The slide show applet uses the getName and System.out.println methods to show which thread is executing. Below is a sample slide show applet run that goes through all the applet's life cycle methods.

As you can see in the sample run, the init, start, stop, and destroy life cycle methods execute in thread applet-SlideShow.class. The run method executes in the Timer thread, and gets called after the Timer thread is created and started in the applet's start method. The Timer thread retrieves images and captions from their arrays and calls the repaint method to initiate drawing to the applet's panel. The applet's update and paint methods do the actual drawing and execute in the AWT-EventQueue-0 thread because updating and painting are panel events.

  init is thread applet-SlideShow.class
  start is thread applet-SlideShow.class
  run is Timer
  update is AWT-EventQueue-0
  paint is AWT-EventQueue-0
  stop is thread applet-SlideShow.class
  start is thread applet-SlideShow.class
  destroy is thread applet-SlideShow.class

New threads are spawned from threads that are already running. The Java VM spawns thread applet-SlideShow class and AWT-EventQueue-0 to handle applet execution and panel events, and thread applet-SlideShow class spawns the Timer thread. The spawning of the Timer thread is what makes this program multithreaded.

Why Use The Timer Thread?

On the surface, it might seem like the applet has enough threads without creating the Timer thread. After all, the applet's start method running in thread applet-SlideShow.class can retrieve the images and text and call repaint, and the update and paint methods running in the AWT-EventQueue-0 thread can do the rendering. Right? Well, not really.

The browser's VM calls into init, start, stop, and destroy and expects them to return after doing whatever it is they have to do. If you change the applet so init or start or a method called by init or start retrieves the images and text, the method will execute forever, never return, and put the applet in a deadlocked state. When that happens the only way to stop the applet is to issue a kill, interrupt, or break signal from the command line. The Timer thread is needed so init or start can return and let the applet paint.

Threaded Applet Anatomy

The SlideShow applet implements the following methods:

Note: This section describes the slide show applet in terms of threads and how the applet's panel is updated with new images and captions. If you need to brush up on applet basics, see the applets lesson in Essentials of the Java Programming Language: A Hands-On Guide.

Initializing the Applet

The init method creates the slide show display and arrays to hold the images and caption text. The images are downloaded from a public Web site, so a URL class provides the URL for the images. This method calls the startThread method, which creates the Timer thread and leaves it in a paused state waiting to be notified to begin execution.

  public void init() {
    images = new Image[3];
    text = new String[3];
    captions = new Label();
    setLayout(new BorderLayout());
    add(BorderLayout.SOUTH, captions);

    Label name = new Label("by Claude Monet");
    name.setAlignment(Label.CENTER);
    add(BorderLayout.EAST, name);

    URL fig = null;
    try {
      fig = new URL(
    "http://developer.java.sun.com:8080/" +
    "developer/technicalArticles/Threads/applet/");
    } catch (java.net.MalformedURLException ex) {
      System.out.println("Bad URL");
      return;
    }

    images[0] = getImage(fig, "bordighera.jpg");
    images[1] = getImage(fig, "etretat.jpg");
    images[2] = getImage(fig, "leyden.jpg");
    text[0] = "Garden in Bordighera";
    text[1] = "Rock Arch West of Etretat";
    text[2] = "Bulbfield and Windmill near Leyden";
    printThreadName("init is ");

    startThread();
  }

The printThreadName("init is ") implementations gets the name of the current thread and prints that name to the command line. The sample run above shows that at this point in the applet's execution the current thread is the applet thread.

init is thread applet-SlideShow.class

Starting the Thread

The startThread method is called once when the applet is first initialized to create timerThread, initialize variables, and implement timerThread behavior. The thread remains in existence until it is stopped in the applet's destroy method.

   private void startThread() {
     paused = true;
     noStopRequested = true;

     // Use this inner class to hide
     // the public run method
     Runnable r = new Runnable() {
       public void run() {
         runWork();
       }
     };
     timerThread = new Thread(r, "Timer");
     timerThread.start();
  }

The line timerThread = new Thread(this, "Timer") means create a thread named Timer and use this object as its target runnable. A target runnable is an object of type Runnable with a run method implementation. So what you are really saying is when the Timer thread starts, execute the run method implementation in this (the SlideShow) object.

The call to timerThread.start() on the next line enlivens the Timer thread by calling the run method in its target runnable. A thread remains alive until the run method returns.

The SlideShow class uses an inner class to create the target runnable and hide the public run method. An inner class is a class nested or defined inside another class, and hiding the public run method implementation in an inner class like this prevents external code from erroneously calling it. Because the run method is public, using an inner class is safer than having the SlideShow class implement the Runnable interface and providing the run method implementation as part of the SlideShow class like this:

  public class SlideShow extends Applet 
		  implements Runnable {
  . . .

  public void run() {
    runWork();
  }

An interface defines a set of methods but does not implement their behavior. Instead, you provide the interface method implementations for the class that implements the interface. The Runnable interface provides the run method as a common protocol for a class instance to execute a separate thread of code. Your implementation for the run method defines what the separate thread of execution needs to do. In this example, the run method implementation calls the runWork method described in Running The Timer Thread below.

Using the Runnable interface is one of two approaches available in the Java programming language for writing multithreaded program. The other approach is to extend the Thread class, which implements the Runnable interface. However, because the Java programming language does not let you extend more than one class, extending the Thread class has its limitations when you need to, for example, extend the Applet class to write an applet or extend the Frame class to write an application with a graphical user interface. For this reason, using the Runnable interface is the more common approach.

The wait-notify mechanism is used to keep the timerThread in the paused state until the applet's start method is called. The wait-notify mechanism allows one thread to wait for a notification from another thread that it can proceed. The paused variable is set to true in the startThread method and checked by the Timer thread when the run method is called. Calls to the applet's start and stop methods toggle the paused variable between false and true, respectively.

Note that the noStopRequested instance variable set to false in the startThread method is declared volatile. The volatile modifier requests the Java VM to always access the shared copy of the variable so the its most current value is always read. Making noStopRequested volatile is necessary so other threads see the changed value. In this example thread applet-SlideShow.class changes the value in its startThread and stopThread methods and the Timer thread reads the value in its run method.

Note: It is important to understand when to use the volatile modifier, and there are some rule you can follow. To quote a section from Java Thread Programming: The Authoritative Solution by Paul Hyde:

If

then

that member variable must be declared volatile to ensure all threads see the changed value. See Pausing the Applet for information on using synchronization.

Running The Timer Thread

The runWork method moves into a while loop if no stop has been requested and stays in the loop until the applet's stopThread method is called where the noStopRequested variable is set to false. Inside the loop the waitWhilePaused method is called to keep the applet in a paused state if the paused instance variable has a value of true.

When the applet first starts, its paused variable is initialized to true and changed to false when the start method is called. It is also set to true when the applet's stop method is called. So the Timer thread sits in the while loop waiting for the applet's start and stop methods to set the values for the paused variable.

When paused is set to false, the Timer thread retrieves images and captions from their arrays and calls the repaint method to update and paint the applet's panel. Between calls to the repaint method and retrieving the next image and caption, the thread sleeps for 3 seconds so each image in the slide show can stay on the display for viewing.

The sleep method throws InterruptedException, which is raised when timerThread.interrupt is called from the stopThread method. The stopThread method is called by the applet's destroy method when the applet exits. The interrupt causes the Timer thread to move out of sleep mode and jump to the catch block where the interrupt is reasserted, a message is printed, and the thread dies.

Note that the curFrame instance variable is declared volatile because it is updated by the runWork method in the Timer thread and read by the update method in thread applet-SlideShow.class.

// Note this method is private
  private void runWork() { 
    printThreadName("run is ");
    curFrame = 0;

    try {
      while ( noStopRequested ) {
        waitWhilePaused();

        curFrame = ( curFrame + 1 ) % 
		images.length;
        repaint();

        Thread.sleep(3000);
      }
    } catch ( InterruptedException x ) {
     // reassert interrupt
     Thread.currentThread().interrupt();
     System.out.println(
	"interrupt and return from run");
    }
  }

The call to printThreadName at the beginning gets the name of the current thread and prints it to the command line. The sample run above shows that at this point in the applet's execution the current thread is the Timer thread:

  run is Timer

Pausing the Applet

The mechanism for pausing the applet consists of the pauseLock object, paused instance variable, setPaused method, and waitWhilePaused method. The paused instance variable is not declared volatile because all threads that update or read it use synchronization.

Synchronization lets one thread safely change values that another thread reads. The setPaused method executed by thread applet-SlideShow.class toggles the paused variable between true and false according to whether the applet is in a stopped, started, or initialized but not yet started state. The waitWhilePaused method executed by the Timer thread reads the value and keeps the applet in a paused state when the paused value is set to true and in an un-paused state when the paused value changes to false. Because both these methods use synchronization, the waitWhilePaused method cannot read the value of the paused variable until the setPaused method finishes setting it and vice versa.

When the Timer thread calls waitWhilePaused, it synchronizes on pauseLock and reads the value of paused. If the value is true, pauseLock.wait is called and the Timer thread goes to sleep. What is happening is the Timer thread is waiting on the pauseLock object to prevent the waitWhilePaused method from returning. If the value is false, the while loop in the Timer thread continues to execute.

If the Timer thread is put to sleep, it stays asleep until it is either interrupted or notified by the setPaused method that paused is now false. When thread applet-SlideShow.class calls setPaused, it synchronizes on pauseLock, changes the value of paused, notifies all waiting threads the value has changed, exits the synchronized block, and returns. When the Timer thread receives the notification, it wakes up, exits its synchronized block, and returns from the waitWhilePaused method.

  private void setPaused(boolean newPauseState) {
    synchronized ( pauseLock ) {
      if ( paused != newPauseState ) {
        paused = newPauseState;
        pauseLock.notifyAll();
      }
    }
  }

  private void waitWhilePaused() 
		throws InterruptedException {
    synchronized ( pauseLock ) {
      while ( paused ) {
        pauseLock.wait();
      }
    }
  }

Object-Level Locks

When code synchronizes on an object, it acquires an object-level lock on that object. This means no other thread can call methods on that object until the lock is released.

The wait methods of the Object class require that an object-level lock be held before they are called. Very soon after the thread gets inside the native code of the wait method, the Java VM releases the lock that thread was holding. Once the waiting thread is notified, times out, or is interrupted, it competes with other threads to reacquire the object-level lock. Once the thread has exclusive access to the object-level lock, it returns from the wait method and continues inside the synchronized block.

A thread must hold an object-level lock on the object before it can call any of the wait, notify, or notifyAll methods on that object. Once a thread gets inside wait, the Java VM temporarily releases the lock to ensure that the signaling is solid and not open to any race conditions. And if the waiting thread did not release the lock, another thread would not be able to call notify or notifyAll because it would not be able to get the lock.

Updating and Painting the Display

The repaint method called from within the run method repaints the applet's panel and calls the applet's paint method to completely redraw the applet's panel.

  public void paint(Graphics g) {
    Thread t = Thread.currentThread();
    String paint = t.getName();
    System.out.println("paint is " + paint);
  }

  public void update(Graphics g) {
    g.drawImage(images[curFrame], 0, 0, this);
    captions.setText(text[curFrame]);
    Thread t = Thread.currentThread();
    String update = t.getName();
    System.out.println("update is " + update);
  }

The call to printThreadName at the beginning gets the name of the current thread and prints it to the command line. The sample run above shows that at this point in the applet's execution the current thread is the AWT-EventQueue-0 thread:

  update is AWT-EventQueue-0
  paint is AWT-EventQueue-0

Stopping the Applet

The applet's stop method is called whenever you minimize applet viewer, or if the applet is running in a Web page, whenever you go to another Web page. At this point the setPaused method is called to put the applet in a paused state.

  public void stop() {
    setPaused(true);
    printThreadName("stop is ");
  }

The last line of the stop method calls printThreadName to get the name of the current thread and print it to the command line. The sample run above shows that at this point in the applet's execution the current thread is the applet thread:

  stop is thread applet-SlideShow.class

Exiting the Applet

When you exit applet viewer or close the browser, the applet's destroy method is called to stop the thread and perform cleanup. The image and text arrays are set to null to make them ready for garbage collection.

The call to images[i].flush flushes all resources used by the Image array, including any pixel data being cached for rendering to the screen and any system resources used to store data or pixels for the images. The images are reset to a state similar to when they were first created so that if they are rendered again, the image data has to be recreated or fetched from its source.

  public void destroy() {
    stopThread();
    for (int i = 0; i < images.length; i++) {
      images[i].flush();
      images[i] = null;
      text[i] = null;
    }
    images = null;
    text = null;
    printthreadname("destroy is ");
  }
}

The last line of the destroy method calls the printThreadName method to get the name of the current thread and print the name to the command line. The sample run above shows that at this point in the applet's execution the current thread is the applet thread:

  destroy is thread applet-SlideShow.class

Conclusion

While it is relatively easy to get started with multithreaded programming in the Java programming language, mastering the use of multiple threads and establishing communications among them takes time, practice, and patience. An examples-based approach is one of the best ways to learn because you reinforce what you read by putting the concepts into practice.

1 As used on this web site, the terms "Java virtual machine" or "JVM" mean a virtual machine for the Java platform.

© 1994-2005 Sun Microsystems, Inc.