Chapter 31

Threads


CONTENTS

When using Windows 95 (and other modern operating systems), you know that you can run several programs simultaneously. This ability is called multitasking. What you may not know is that many of today's operating systems also allow threads, which are separate processes that are kind of a step down from a complete application. A thread is a lot like a subprogram. An applet can create several threads-several different flows of execution-and run them concurrently. This is a lot like having multitasking inside multitasking. The user knows that he or she can run several applications at a time. The programmer knows that each application can run several threads at a time. In this chapter, you'll learn how to create and manage threads in your own applets.

Two Kinds of Threads

In Java, you can create threads in a couple of ways. The simplest way is to take an existing class and turn it into a thread. You do this by modifying the class so that it implements the Runnable interface, which declares the run() method required by all types of threads. (The run()method contains the code to be executed by a thread.) In the previous chapter, you learned how interfaces in Java enable you to add capabilities to classes simply by implementing the interface in that class. Now, you get a chance to put that idea to work for real.

The second way to create a thread is to write a completely separate class derived from Java's Thread class. Because the Thread class itself implements the Runnable interface, it already contains a run() method. However, Thread's run() method doesn't do anything. You usually have to override the method in your own class in order to create the type of thread you want.

Converting a Class to a Thread

As I mentioned in the preceding section, the first way to create a thread is to convert a class to a thread. To do this, you must perform several steps, as listed here:

  1. Declare the class as implementing the Runnable interface.
  2. Implement the run() method.
  3. Declare a Thread object as a data field of the class.
  4. Create the Thread object and call its start() method.
  5. Call the thread's stop() method to destroy the thread.

The following sections look at each of these steps in detail.

Declaring the Class as Implementing the Runnable Interface

As you can see in step 1 in the preceding section, to create a thread from a regular class, the class must first be declared as implementing the Runnable interface. For example, if your class is declared as


public class MyApplet extends Applet 

you must change that declaration to


public class MyApplet extends Applet 

    implements Runnable

Implementing the run() Method

Now, because you've told Java you're about to implement an interface, you must implement every method in the interface. In the case of Runnable, that's easy because there's only one method, run(), the basic implementation of which looks like this:


public void run()

{

}

When you start your new thread, Java calls the thread's run() method, so it is in run() where all the action takes place. The preceding example of the run() method is the minimum you need to compile the new source code for the thread. However, in a real program, you'll add code to run() so that the thread does what you want it to do.

Declaring a Thread Object

The next step is to declare a Thread object as a data field of the class, like this:


Thread thread;

The thread object will hold a reference to the thread with which the applet is associated. You will be able to access the thread's methods through this object.

Creating and Starting the Thread Object

Now it's time to write the code that creates the thread and gets it going. Assuming that your new threaded class is an applet, you'll often want to create and start the thread in the applet's start() method, as shown in Listing 31.1.


Listing 31.1  LST31_1.TXT: Creating and Starting a Thread Object.

public void start()

{

    thread = new Thread(this);

    thread.start();

}


NOTE
Back in Chapter 15, "Writing a Simple Applet," you learned that start() is the method that represents the applet's second life-cycle stage. Java calls your applet's life-cycle methods in this order: init(), start(), paint(), stop(), and destroy(). Java calls the start() method whenever the applet needs to start running, usually when it's first loaded or when the user has switched back to the applet from another Web page.

Look at the call to the Thread constructor in Listing 31.1. Notice that the constructor's single argument is the applet's this reference. This is how Java knows with which class to associate the thread. Right after the call to the constructor, the applet calls the Thread object's start() method, which starts the thread running. When the thread starts running, Java calls the thread's run() method, where the thread's work gets done.

Stopping the Thread

When the thread's run() method ends, so does the thread. However, because threads tend to run for quite a while, controlling things like animation in the applet, the user is likely to switch away from your applet before the thread stops. In this case. it's up to your applet to stop the thread. Because Java calls an applet's stop() method whenever the user switches away from the applet, the stop() method is a good place to stop the thread, as shown in Listing 31.2.


Listing 31.2  LST31_2.TXT: Stopping a Thread.

public void stop()

{

    thread.stop();

}


Example: Using a Thread in an Applet

To understand about threads, you really have to dig in and use them. So in this section, you'll put together an applet that associates itself with a Thread object and runs the thread to control a very simple animated display. The animation in this case is not a bunch of space invaders landing to take over the earth, but rather only a changing number that proves that the thread is running. Listing 31.3 is the applet in question, which is called ThreadApplet. Figure 31.1 shows the applet running under Appletviewer.

Figure 31.1 : ThreadApplet uses a thread to count to 1,000.


Listing 31.3  ThreadApplet.java: Using a Thread in an Applet.

import java.awt.*;

import java.applet.*;



public class ThreadApplet extends Applet 

  implements Runnable

{

    Thread thread;

    int count;

    String displayStr;

    Font font;



    public void start()

    {

        font = new Font("TimesRoman", Font.PLAIN, 72);

        setFont(font);



        count = 0;

        displayStr = "";



        thread = new Thread(this);

        thread.start();

    }



    public void stop()

    {

        thread.stop();

    }

  

    public void run()

    {



        while (count < 1000)

        {

            ++count;

            displayStr = String.valueOf(count);

            repaint();



            try

            {

                thread.sleep(100);

            }

            catch (InterruptedException e)

            {

            }

        }

    }



    public void paint(Graphics g)

    {

        g.drawString(displayStr, 50, 130);

    }

}


Tell Java that the applet uses the classes in the awt package.
Tell Java that the applet uses the classes in the applet package.
Derive ThreadApplet from Applet and implement Runnable.
Declare the class's data fields, including a Thread object.
Override the start() method.
Create and set the applet's display font.
Initialize data fields.
Create and start the thread.
Override the stop() method.
Stop the thread.
Implement the run() method
Loop one thousand times.
Increment the counter.
Create the display string from the counter.
Tell Java to repaint the applet.
Suspend the thread for one hundred milliseconds.
Override the paint() method.
Draw the display string.

There are a couple of interesting things in ThreadApplet of which you should be aware. First, notice that in run(), the thread loops one thousand times, after which the while loop ends. When the while loop ends, so does the run() method. This means that when you run ThreadApplet, if you let it count all the way to one thousand, the thread ends on its own. However, what if you switch to a different Web page before ThreadApplet has counted all the way to one thousand? Then, Java calls the applet's stop() method, which ends the thread by calling the thread's stop() method.

The next point of interest is what's going on inside run(). At the beginning of the loop, the program increments the counter, converts the counter's value to a string, and then repaints the applet so that the new count value appears in the window. That code should be as clear as glass to you by now. But what's all that malarkey after the call to repaint()? That's where the thread not only times the animation, but also relinquishes the computer so that other threads get a chance to run. Simply, the call to the thread's sleep() method suspends the thread for the number of milliseconds given as its single argument. In this case, the sleep time is 100 milliseconds, or one tenth of a second. If you want the animation to run faster, change the 100 to a smaller value. To count slower, change the 100 to a larger value.

CAUTION
It's important that your threads not dominate the computer's processor for longer than necessary. This is because other threads and processes are almost certainly in competition for the processor at the same time. If your thread will be running for a while, you should call the sleep() or yield() methods in order to give other processes a chance to run. This is more important on some systems than on others, but since you can't know for sure which system your applet will be running on, be a considerate thread programmer.

Notice that the call to sleep() is enclosed in a try block and followed by a catch block that's watching for InterruptedException exceptions. You have to catch this exception because the sleep() method throws it. If you fail to catch the exception, your program will not compile.

Deriving a Class from Thread

The second way to create a thread is to derive a new class from Thread. Then, in your applet's class, you create and start a thread object of your thread class. This leaves you with two processes going simultaneously, the applet and the thread object created in the class. By giving the thread class access to data and methods in the applet, the thread can easily communicate with the applet in order to perform whatever tasks it was written for.

Example: Creating a Thread Class

Suppose that you want to write the same sort of applet as that shown in Listing 31.3, but now you want a separate thread to control the counting process. Listing 31.4 shows how you might write the new class for the thread. (Don't try to compile this code yet. You'll use it in the next example in this chapter.)


Listing 31.4  MyThread.java: A Class Derived from Thread.

public class MyThread extends Thread

{

    ThreadApplet2 applet;

    int count;



    MyThread(ThreadApplet2 applet)

    {

        this.applet = applet;

    }



    public void run()

    {

        count = 0;

        while (count < 1000)

        {

            ++count;

            applet.displayStr = String.valueOf(count);

            applet.repaint();



            try

            {

                sleep(100);

            }

            catch (InterruptedException e)

            {

            }

        }

    }

}


Derive the MyThread class from Thread.
Declare the class's data fields, including a Thread object.
Declare the class's constructor.
Store the constructor's single parameter.
Override the run() method
Loop one thousand times.
Increment the counter.
Create the display string from the counter.
Tell Java to repaint the applet.
Suspend the thread for one hundred milliseconds.

The first thing to notice in this thread class is that its constructor takes as a single argument a reference to a ThreadApplet2 object, which is the applet from which you'll be running this thread. The thread needs this reference so that it can communicate with the applet.

Next, look at run(). The thread still counts from zero to one thousand, but now it accesses the applet object in order to create the display string and repaint the applet. In the original version of the program, the thread was directly associated with the class, rather than a completely separate process.

Now that you have a new thread class, you'll want to call it up for active duty. You'll do that in the next example.

Example: Using a Separate Thread in an Applet

You'll now put that new thread class to work. To do this, you must have an applet that creates an object from the new thread class and calls that object's start() method to get the thread running. Listing 31.5 shows just such an applet, called ThreadApplet2. When you run the applet under Appletviewer, you'll see the same display that was created in the original version of the applet (ThreadApplet), but now the counting animation is being controlled by a separate thread class.

NOTE
To compile Listing 31.5, make sure you have both the MyThread.java and ThreadApplet2.java files in your CLASSES folder. Java will then compile both files when you compile ThreadApplet2.java.


Listing 31.5  ThreadApplet2.JAVA: An Applet That Creates a Separate Thread.

import java.awt.*;

import java.applet.*;

import MyThread;



public class ThreadApplet2 extends Applet

{

    MyThread thread;

    String displayStr;

    Font font;



    public void start()

    {



        font = new Font("TimesRoman", Font.PLAIN, 72);

        setFont(font);



        displayStr = "";



        thread = new MyThread(this);

        thread.start();

    }



    public void stop()

    {

        thread.stop();

    }



    public void paint(Graphics g)

    {

        g.drawString(displayStr, 50, 150);

    }

}


Tell Java that the applet uses the classes in the awt package.
Tell Java that the applet uses the classes in the applet package.
Tell Java that the applet uses the MyThread class.
Derive the ThreadApplet2 class from Applet.
Declare the class's data fields, including a MyThread object.
Override the start() method
Create and set the applet's font.
Initialize the display string.
Create and start the thread.
Override the stop() method.
Stop the thread.
Override the paint() method.
Draw the applet's display string, which is the current count.

Synchronizing Multiple Threads

There may be times when you have several threads going, each competing for the same resources. This type of resource competition can be deadly for threads. For example, what if one thread tries to read from a string while another thread is still writing to that string? Depending on the situation, you'll get strange results. You can avoid these problems by telling Java where synchronization problems may occur so that Java can keep an eye out for unhealthy thread competition.

To put Java on guard, you use the synchronized keyword when you define a method (or even a code block). When you mark a method as synchronized, Java creates a monitor object for the class. The first time a thread calls the synchronized method, Java gives the monitor object to that thread. As long as the thread holds the monitor object, no other thread can enter the synchronized section of code. You can think of the monitor object as a key. Unless a thread is holding the key, it can't unlock the door to the synchronized method.

Example: Using a Synchronized Method

Using synchronized methods makes sense only when more than one thread is vying for an applet's resources. For that reason, to demonstrate thread synchronization, you need to create two threads. Listing 31.6 is a thread class, called MyThread2, that can count either forward or backward, depending upon the values you give to the class's constructor. By creating two thread objects from this class, you can experiment with thread synchronization.

NOTE
To compile Listings 31.6 and 31.7, make sure you have both the MyThread2.java and ThreadApplet3.java files in your CLASSES folder. Java will then compile both files when you compile ThreadApplet3.java.


Listing 31.6  MyThread2.java: A Double-Duty Thread.

public class MyThread2 extends Thread

{

    ThreadApplet3 applet;

    boolean forward;

    int count;

    int increment;

    int end;

    int position;



    MyThread2(ThreadApplet3 applet, boolean forward)

    {

        this.applet = applet;

        this.forward = forward;

    }



    public void run()

    {

        InitCounter();

        DoCount();

    }



    protected void InitCounter()

    {

        if (forward)

        {

            count = 0;

            increment = 1;

            end = 1000;

            position = 120;

        }

        else

        {

            count = 1000;

            increment = -1;

            end = 0;

            position = 180;

        }

    }





    protected void DoCount()

    {



        while (count != end)

        {

            count = count + increment;

            String str = String.valueOf(count);

            applet.SetDisplayStr(str, position);



            try

                sleep(100);

            catch (InterruptedException e)

            {

            }

        }

    }

}


Derive the MyThread2 class from Thread.
Declare the class's data fields.
Declare the class's constructor.
Store the constructor's parameters.
Override the run() method
Call the method that sets the values for this thread.
Call the method that does the counting.
Define the InitCounter() method.
If the thread is to count forward...
Set the data fields for forward counting.
Else if the thread is to count backwards...
Set the data fields for backwards counting.
Define the DoCount() method.
Loop until the counting is done.
Increment the counter and set the display string.
Go to sleep for one hundred milliseconds.

When you construct a MyThread2 thread object, you must pass two values as parameters: a reference to the applet and a boolean value indicating whether the thread should count forward or backward. The thread uses the boolean value in its InitCounter() method to set the values needed to accomplish the counting. These values are the starting count value (count), the counting increment (increment), the target count (end), and the position at which to display the count in the applet (position). Notice that the increment variable can be either 1 or -1. When the increment gets added to the count, a positive increment increases the count by one, whereas a negative increment decreases the count by one.

In its run() method, the thread calls the applet's SetDisplayStr() method, which, as you'll soon see, is the synchronized method. In other words, if the thread isn't holding the monitor object for SetDisplayStr(), it cannot enter the method. This prevents two running instances of the MyThread2 thread from trying to change the display string at the same time.

Now it's time to look at the applet that's in charge of the threads. Listing 31.7 is the applet, which is called ThreadApplet3. This applet creates two objects of the MyThread2 class: one that counts forward and one that counts backward. The applet's SetDisplayStr() method is where the synchronization comes into play because both threads will be trying to access this method.

When you run the applet, you'll see that when the first thread can display its count, the string will appear closer to the top of the display area. The second thread, however, displays its count below the first thread's. For this reason, when you get the applet going, you can sit back and watch the two threads battle over the SetDisplayStr() method.


Listing 31.7  ThreadApplet3.java: An Applet That Uses Thread Synchronization.

import java.awt.*;

import java.applet.*;

import MyThread2;



public class ThreadApplet3 extends Applet

{

    MyThread2 thread1;

    MyThread2 thread2;

    String displayStr;

    Font font;

    int position;



    public void init()

    {

        font = new Font("TimesRoman", Font.PLAIN, 72);

        setFont(font);



        displayStr = "";

        position = 120;



        thread1 = new MyThread2(this, true);

        thread2 = new MyThread2(this, false);

    }



    public void start()

    {

        if (thread1.isAlive())

            thread1.resume();

        else

            thread1.start();



        if (thread2.isAlive())

            thread2.resume();
else thread2.start(); } public void stop() { thread1.suspend(); thread2.suspend(); } public void destroy() { thread1.stop(); thread2.stop(); } public void paint(Graphics g) { g.drawString(displayStr, 50, position); } synchronized public void SetDisplayStr(String str, int pos) { displayStr = str; position = pos; repaint(); } }

Tell Java that the applet uses the classes in the awt package.
Tell Java that the applet uses the classes in the applet package.
Tell Java that the applet uses the MyThread2 class.
Derive the ThreadApplet3 class from Applet.
Declare the class's data fields, including two MyThread2 objects.
Override the init() method.
Create and set the applet's font.
Initialize the display string and display position.
Create the applet's two threads.
Override the start() method
If the first thread is already started...
Resume running the thread.
Else if the first thread hasn't yet been started...
Start the thread.
If the second thread is already started...
Resume running the thread.
Else if the second thread hasn't yet been started...
Start the thread.
Override the stop() method.
Suspend both threads.
Override the destroy() method.
Stop both threads.
Override the paint() method.
Draw the applet's display string, which is the current count.
Define the SetDisplayStr() method as synchronized.
Copy the method's parameters into the class's data fields.
Force Java to redraw the applet's display.

Understanding ThreadApplet3

The ThreadApplet3 applet is unique with regards to other applets in this book because it's the only applet that takes full advantage of the applet's life-cycle stages. In the init() method, the applet creates the two threads. The different boolean values given as the constructor's second argument cause the first thread to count forward and the second thread to count backward.

In the start() method, the applet calls each thread's isAlive() method to determine whether the thread has been started yet. The first time start() gets called, the threads have been created in init() but haven't been started. In this case, isAlive() returns false, and the applet calls each thread's start() method to get the threads rolling. If start() is not being called for the first time, it's because the user has switched back to the applet from another Web page. In this case, isAlive() returns true. The applet knows that it must call the threads' resume() method rather than start().

In the stop() method, which gets called when the user switches to another Web page, rather than stopping the threads, the applet suspends them. The threads remain suspended until the applet calls their resume() methods, which, as you now know, happens in the start() method.

Finally, when Java calls the destroy() method, the applet is going away for good. The threads, too, should follow suit, so the applet calls each thread's stop() method.

CAUTION
When programming threads, you always have to watch out for a condition known as deadlock. Deadlock occurs when two or more threads are waiting to gain control of a resource, but for one reason or another, the threads rely on conditions that can't be met in order to get control of the resource. To understand this situation, imagine that you have a pencil in your hand, and someone else has a pen. Now, assume that you can't release the pencil until you have the pen, and the other person can't release the pen until she has the pencil. Deadlock! A more computer-oriented example would be when one thread must access Method1 before it can release its hold on Method2, but the second thread must access Method2 before it can release its hold on Method1. Because these are mutually exclusive conditions, the threads are deadlocked and cannot run.

Summary

Threads enable you to break an applet's tasks into separate flows of execution. These subprograms seem to run concurrently thanks to the task switching that occurs in multitasking systems. You can create a thread from a class by implementing the Runnable interface in the class. However, you can also create a separate class for your threads by deriving the class from Thread. Depending on how you want to use the threads, you can create and start your threads in the applet's start() method and stop the threads in the stop() method. If you want your threads to retain their state when the user switches to and from your Web page, you should create the threads in init(), start or resume the threads in start(), suspend the threads in stop(), and stop the threads in destroy(). Remember that if there's a chance that two or more threads may compete for a resource, you need to protect that resource using thread synchronization.

Review Questions

  1. How are threads similar to multitasking?
  2. What Java interface must be implemented by all threads?
  3. What thread method do you call to start a thread?
  4. What method does Java call to get a thread started?
  5. What are the two applet methods in which you'll usually stop your threads?
  6. What's the difference between suspending and stopping a thread?
  7. How do you ensure that your threads share the computer's processor properly?
  8. If you don't care about retaining a thread's state as the user switches between Web pages, where in your applet should you create and start your threads?
  9. How can you take advantage of an applet's life cycle in order to retain a thread's state as the user switches between Web pages.
  10. When would you use the synchronized keyword?
  11. What's a monitor object?

Review Exercises

  1. Modify the ThreadApplet applet so that the applet's state is retained when switching to and from the applet's Web page. Name the new version ThreadApplet4. (You can find the solution to this exercise in the CHAP31 folder of this book's CD-ROM.)
  2. Modify the ThreadApplet2 applet so that the thread changes the color of three rectangles displayed in the applet (see Figure 31.2). The rectangles' colors should cycle between red, green, and blue. Repeat the color cycling until the user stops the applet. Name the applet ThreadApplet5, and name the new thread class ColorThread. (You can find the solution to this exercise in the CHAP31 folder of this book's CD-ROM.)
    Figure 31.2 : Your TheadApplet5 applet should look like this when running under Appletviewer.

  3. Modify your ThreadApplet5 applet (naming the new applet ThreadApplet6) so that it runs two threads. One thread should change the rectangles' colors to red, green, and blue, and the second thread should change the rectangles to pink, orange, and yellow. Modify the ColorThread class from exercise 2, renaming it ColorThread2, and then create a new thread class called ColorThread3 for setting the second set of colors. Don't forget to use thread synchronization to prevent one thread from changing the rectangles' colors when another thread is already doing so. (You can find the solution for this exercise in the CHAP31 folder of this book's CD-ROM.)