Exploring Java

Previous Chapter 6
Threads
Next
 

6.2 Threading Applets

Applets are embeddable Java applications that are expected to be able to start and stop themselves on command. Unlike threads, applets can be started and stopped any number of times. A Java-enabled Web browser normally starts an applet when the applet is displayed and stops it when the user moves to another page or scrolls the applet out of view. In general, we would like an applet to cease its nonessential activity when it is stopped, and resume it when started again. (See Chapter 10, Understand the Abstract Windowing Toolkit for a complete discussion of applets.)

In this section, we will build UpdateApplet, a simple base class for an Applet that maintains a Thread to automatically update its display at regular intervals. UpdateApplet handles the basic starting and stopping behavior for us, as shown below.

public class UpdateApplet extends java.applet.Applet 
    implements Runnable { 
    
    private Thread updateThread; 
    int updateInterval = 1000; 
 
    public void run() { 
        while ( true ) { 
            try {   
                Thread.sleep( updateInterval );  
            }  
            catch (InterruptedException e ) { } 
 
            repaint(); 
        } 
    } 
 
    public void start() { 
        if ( updateThread == null ) { 
            updateThread = new Thread(this); 
            updateThread.start(); 
        } 
    } 
 
    public void stop() { 
        if ( updateThread != null ) { 
            updateThread.stop(); 
            updateThread = null; 
        } 
    } 
} 

UpdateApplet is a Runnable object that alternately sleeps and calls its repaint() method. It has two other public methods: start() and stop(). These are methods of the Applet class we are overriding; do not confuse them with the similarly named methods of the Thread class. These start() and stop() methods are called by the Java environment to tell the applet when it should and should not be running.

UpdateApplet illustrates an environmentally friendly way to deal with threads in a simple applet. UpdateApplet kills its thread each time the applet is stopped and recreates it if the applet is restarted. When UpdateApplet's start() method is called, we first check to make sure there is no currently executing updateThread. We then create one to begin our execution. When our applet is subsequently stopped, we kill the thread by invoking its stop() method and throw away the reference by setting it to null. Setting updateThread to null serves both to allow the garbage collector to clean up the dead Thread object, and to indicate to UpdateApplet's start() method that the thread is gone.

In truth, an Applet's start() and stop() methods are guaranteed to be called in sequence. As a result, we shouldn't have to check for the existence of updateThread in start() (it should always be null). However, it's good programming practice to perform the test. If we didn't, and for some reason stop() were to fail at its job, we might inadvertently start a lot of threads.

With UpdateApplet doing all of the work for us, we can now create the world's simplest clock applet with just a few lines of code. Figure 6.3 shows our Clock. (This might be a good one to run on your Java wrist watch.)

public class Clock extends UpdateApplet { 
    public void paint( java.awt.Graphics g ) { 
        g.drawString( new java.util.Date().toString(), 10, 25 ); 
    } 
} 

Figure 6.3: The clock applet

[Graphic: Figure 6-3]

The java.util.Date().toString() sequence simply creates a string that contains the current time; we'll see where this code comes from in Chapter 7, Basic Utility Classes.

Our Clock applet provides a good example of a simple thread; we don't mind throwing it away and subsequently rebuilding it if the user should happen to wander on and off of our Web page a few times. But what if the task that our thread handles isn't so simple? What if, for instance, we have to open a socket and establish a connection with another system? One solution is to use Thread's suspend() and resume() methods, as I'll show you momentarily.

Now if you've been wondering why we've been using stop() to kill the thread, rather than using the suspend() and resume() methods all along, here's the explanation you've been waiting for. The problem with applets is that we have no control over how a user navigates Web pages. For example, say a user scrolls our applet out of view, and we use suspend() to suspend the applet. Now we have no way of ensuring that the user will bring the applet back into view before moving on to another page. And actually, the same situation would occur if the user simply moves on to another page and never comes back.

If we call suspend(), we'd really like to make sure we call resume() at a later date, or we'll end up leaving the thread hanging in permanent suspense. But we have no way of knowing if the applet will ever be restarted, so just putting a call to resume() in the applet's start() method won't work. Leaving the suspended thread around forever might not hurt us, but it's not good programming practice to be wasteful. What we need is a way to guarantee we can clean up our mess if the applet is never used again. What to do?

There is a solution for this dilemma, but in many cases, like with our simple Clock, it's just easier to use stop(), with a subsequent call to start() if necessary. In cases where it is expensive to set up and tear down a thread, we could make the following modifications to UpdateApplet:

public void start() { 
    if ( updateThread == null ) { 
        updateThread = new Thread(this); 
        updateThread.start(); 
    } 
    else 
        updateThread.resume(); 
} 
 
public void stop() { 
    updateThread.suspend(); 
 
public void destroy() { 
    if ( updateThread != null ) { 
        updateThread.stop(); 
        updateThread = null; 
    } 
} 

These modifications change UpdateApplet so that it suspends and restarts its updateThread, rather than killing and recreating it. The new start() method creates the thread and calls start() if updateThread is null; otherwise it assumes that the thread has been suspended, so it calls resume(). The applet's stop() method simply suspends the thread by calling suspend().

What's new here is the destroy() method. This is another method that UpdateApplet inherits from the Applet class. The method is called by the Java environment when the applet is going to be removed (often from a cache). It provides a place where we can free up any resources the applet is holding. This is the perfect place to cut the suspense and clean up after our thread. In our destroy() method, we check to see that the thread exists, and if it does, we call stop() to kill it and set its reference to null.


Previous Home Next
Introducing Threads Book Index Synchronization

Java in a Nutshell Java Language Reference Java AWT Java Fundamental Classes Exploring Java