The JavaTM Tutorial
Previous Page Lesson Contents Next Page Start of Tutorial > Start of Trail > Start of Lesson Search
Feedback Form

Trail: Essential Java Classes
Lesson: Threads: Doing Two or More Tasks at Once

The Life Cycle of a Thread

Now that you've seen how to give a thread something to do, let's review some details that were glossed over in the previous section. In particular, we look at how to create and start a thread, some of the special things it can do while it's running, and how to stop it.

The following figure shows the states a thread can be in during its life and illustrates which method calls cause a transition to another state. This figure is not a complete finite state diagram but rather an overview of the more interesting and common facets of a thread's life. The remainder of this section uses the previously introduced Clock applet to discuss a thread's life cycle in terms of its state.

Thread states.

Thread states.

Creating a Thread

The application in which an applet is running calls the applet's start method when the user visits the applet's page. The Clock applet creates a Thread, clockThread, in its start method with the code shown in boldface.
public void start() {
    if (clockThread == null) {
        clockThread = new Thread(this, "Clock");
        clockThread.start();
    }
}    
After the statement has been executed, clockThread is in the New Thread state. A thread in this state is merely an empty Thread object; no system resources have been allocated for it yet. When a thread is in this state, you can only start the thread. Calling any method besides start when a thread is in this state makes no sense and causes an IllegalThreadStateException. In fact, the runtime system throws an IllegalThreadStateException whenever a method is called on a thread and that thread's state does not allow for that method call.

Note that the Clock instance is the first argument to the thread constructor. The first argument to this thread constructor must implement the Runnable interface and becomes the thread's target. The clockThread gets its run method from its target Runnable object — in this case, the Clock instance. The second argument is just a name for the thread.

Starting a Thread

Now consider the next line of code in Clock's start method, shown here in boldface.
public void start() {
    if (clockThread == null) {
        clockThread = new Thread(this, "Clock");
        clockThread.start();
    }
}  
The start method creates the system resources necessary to run the thread, schedules the thread to run, and calls the thread's run method. clockThread's run method is defined in the Clock class.

After the start method has returned, the thread is "running"; however, it's somewhat more complex than that. As the previous figure shows, a thread that has been started is in the Runnable state. Many computers have a single processor, thus making it impossible to run all running threads at the same time. The Java runtime system must implement a scheduling scheme that shares the processor among all running threads; see the Thread Scheduling (in the Essential Java Classes trail) section for more information about scheduling. So, at any given time, a running thread may be waiting for its turn in the CPU.

Here's another look at Clock's run method.

public void run() {
   Thread myThread = Thread.currentThread();
   while (clockThread == myThread) {
        repaint();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            //The VM doesn't want us to sleep anymore,
            //so get back to work.
        }
    }
}
Clock's run method loops while the condition clockThread == myThread is true. This exit condition is explained in more detail in the Stopping a Thread section. For now, however, know that it allows the thread, and thus the applet, to exit gracefully.

Within the loop, the applet repaints itself and then tells the thread to sleep for one second (1,000 milliseconds). An applet's repaint method ultimately calls the applet's paint method, which does the actual update of the applet's display area. The Clock paint method gets the current time, formats it, and displays.

public void paint(Graphics g) {
    //Get the time and convert it to a date.
    Calendar cal = Calendar.getInstance();
    Date date = cal.getTime();
    //Format and display it.
    DateFormat dateFormatter = DateFormat.getTimeInstance();
    g.drawString(dateFormatter.format(date), 5, 10);
}

Making a Thread Not Runnable

A thread enters Not Runnable state when one of the following events occurs: The clockThread in the Clock applet becomes Not Runnable when the run method calls sleep on the current thread.
public void run() {
    Thread myThread = Thread.currentThread();
    while (clockThread == myThread) {
        repaint();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
           //The VM doesn't want us to sleep anymore,
           //so get back to work.
        }
    }
}
During the second that the clockThread is asleep, the thread does not run, even if the processor becomes available. After the second has elapsed, the thread becomes Runnable again; if the processor becomes available, the thread begins running again.

For each entrance into the Not Runnable state, a specific and distinct exit returns the thread to the Runnable state. An exit works only for its corresponding entrance. For example, if a thread has been put to sleep, the specified number of milliseconds must elapse before the thread becomes Runnable again. The following list describes the exit for every entrance into the Not Runnable state:

Stopping a Thread

Although the Thread class does contain a stop method, this method is deprecated and should not be used to stop a thread because it is unsafe. Rather, a thread should arrange for its own death by having a run method that terminates naturally. For example, the while loop in this run method is a finite loop: It will iterate 100 times and then exit.
public void run() {
    int i = 0;
    while (i < 100) {
        i++;
        System.out.format("i = %d%n", i);
    }
}
A thread with this run method dies naturally when the loop completes and the run method exits.

Let's look at how the Clock applet thread arranges for its own death. (You might want to use this technique with your applet threads.) Recall the code for the Clock's run method.

public void run() {
    Thread myThread = Thread.currentThread();
    while (clockThread == myThread) {
        repaint();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            //The VM doesn't want us to sleep anymore,
            //so get back to work.
        }
    }
}
The exit condition for this run method is the exit condition for the while loop because there is no code after the while loop.
while (clockThread == myThread) {
This condition indicates that the loop will exit when the currently executing thread is not equal to clockThread. When would this ever be the case?

When you leave the page, the application in which the applet is running calls the applet's stop method. This method then sets the clockThread to null, thereby telling the main loop in the run method to terminate.

public void stop() {    //applet's stop method
    clockThread = null;
}
If you revisit the page, the start method is called again and the clock starts up again with a new thread. Even if you stop and start the applet faster than one iteration of the loop, clockThread will be a different thread from myThread and the loop will still terminate.

Testing Thread State

A final word about Thread state: Release 5.0 introduced the Thread.getState method. When called on a thread, one of the following Thread.State values is returned: The API for the Thread class also includes a method called isAlive. The isAlive method returns true if the thread has been started and not stopped. If the isAlive method returns false, you know that the thread either is a New Thread or is Dead. If the isAlive method returns true, you know that the thread is either in a Runnable or Not Runnable state.

Prior to release 5.0, you couldn't differentiate between a New Thread or a Dead thread; nor could you differentiate between a Runnable thread and a Not Runnable thread.


Previous Page Lesson Contents Next Page Start of Tutorial > Start of Trail > Start of Lesson Search
Feedback Form

Copyright 1995-2005 Sun Microsystems, Inc. All rights reserved.