The Life Cycle of a Thread

So far, we have a simple knowledge of working with threads: we know how to use the start() method to start a thread, and how to terminate a thread by arranging for its run() method to complete. We’ll now look at two techniques that provide us more information about the thread during its life cycle.

The isAlive() Method

There is a period of time after you call the start() method before the virtual machine can actually start the thread. Similarly, when a thread returns from its run() method, there is a period of time before the virtual machine can clean up after the thread; and if you use the stop() method, there is an even greater period of time before the virtual machine can clean up after the thread.

This delay occurs because it takes time to start or terminate a thread; therefore, there is a transitional period from when a thread is running to when a thread is not running, as shown in Figure 2.3. After the run() method returns, there is a short period of time before the thread stops. If we want to know if the start() method of the thread has been called—or, more usefully, if the thread has terminated—we must use the isAlive() method. This method is used to find out if a thread has actually been started and has not yet terminated:

boolean isAlive()

Determines if a thread is considered alive. By definition, a thread is considered alive from sometime before a thread is actually started to sometime after a thread is actually stopped.

Graphical representation of the states of the thread

Figure 2-3. Graphical representation of the states of the thread

Let’s modify our Animate class to wait until the timer thread stops before finishing:

import java.applet.*;
import java.awt.*;

public class Animate extends Applet {
    int count, lastcount;
    Image pictures[];
    TimerThread timer;

    public void init() {
        lastcount = 10; count = 0;
        pictures = new Image[10];
        MediaTracker tracker = new MediaTracker(this);
        for (int a = 0; a < lastcount; a++) {
            pictures[a] = getImage(
                getCodeBase(), new Integer(a).toString()+".jpeg");
            tracker.addImage(pictures[a], 0);
        }
        tracker.checkAll(true);
    }

    public void start() {
        timer = new TimerThread(this, 1000);
        timer.start();
    }

    public void stop() {
        timer.shouldRun = false;
        while (timer.isAlive()) {
                           try {
                               Thread.sleep(100);
                           } catch (InterruptedException e) {}

                       }
        timer = null;
    }

    public void paint(Graphics g) {
        g.drawImage(pictures[count++], 0, 0, null);

        if (count == lastcount) count = 0; 
    }
}

Just because a thread has been started does not mean it is actually running, nor that it is able to run—the thread may be blocked, waiting for I/O, or it may still be in the transitional period of the start() method. For this reason, the isAlive() method is more useful in detecting whether a thread has stopped running. For example, let’s examine the stop() method of this applet. Just like the earlier versions, we have a TimerThread object that is started and stopped when the applet is started and stopped. In this newer version, the applet’s stop() method does more than just stop the TimerThread: it also checks to make sure the thread actually has stopped.

In this example, we don’t gain anything by making sure the timer thread has actually stopped. But if for some reason we need to deal with common data that is being accessed by two threads, and it is critical to make sure the other thread is stopped, we can simply loop and check to make sure the thread is no longer alive before continuing.

There is another circumstance in which a thread can be considered no longer alive: if the stop() method is called, the thread will be considered no longer alive a short time later. This is really the same case: the isAlive() method can be used to determine if the run() method has completed, whether normally or as a result of the stop() method having been called.

Joining Threads

The isAlive() method can be thought of as a crude form of communication. We are waiting for information: the indication that the other thread has completed. As another example, if we start a couple of threads to do a long calculation, we are then free to do other tasks. Assume that sometime later we have completed all other secondary tasks and need to deal with the results of the long calculation: we need to wait until the calculations are finished before continuing on to process the results.

We could accomplish this task by using the looping isAlive() technique we’ve just discussed, but there are other techniques in the Java API that are more suited to this task. This act of waiting is called a thread join. We are “joining” with the thread that was “forked” off from us earlier when we started the thread. So, modifying our last example, we have:

import java.applet.Applet;

public class Animate extends Applet {
    ...
    public void stop() {
        t.shouldRun = false;
        try {
               t.join();
        } catch (InterruptedException e) {}
    }
}

The Thread class provides the following join() methods:

void join()

Waits for the completion of the specified thread. By definition, join() returns as soon as the thread is considered “not alive.” This includes the case in which the join() method is called on a thread that has not been started.

void join(long timeout)

Waits for the completion of the specified thread, but no longer than the timeout specified in milliseconds. This timeout value is subject to rounding based on the capabilities of the underlying platform.

void join(long timeout, int nanos)

Waits for the completion of the specified thread, but no longer than a timeout specified in milliseconds and nanoseconds. This timeout value is subject to rounding based on the capabilities of the underlying platform.

When the join() method is called, the current thread will simply wait until the thread it is joining with is no longer alive. This can be caused by the thread not having been started, or having been stopped by yet another thread, or by the completion of the thread itself. The join() method basically accomplishes the same task as the combination of the sleep() and isAlive() methods we used in the earlier example. However, by using the join() method, we accomplish the same task with a single method call. We also have better control over the timeout interval, and we don’t waste CPU cycles by polling.

Another interesting point about both the isAlive() method and the join() method is that we are actually not affecting the thread on which we called the method. That thread will run no differently whether the join() method is called or not; instead, it is the calling thread that is affected. The isAlive() method simply returns the status of a thread, and the join() method simply waits for a certain status on the thread.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset