Friday, March 23, 2012

Chapter 4: The Lifecycle of a Thread

Now that we know how to create a thread, the next step in the learning process it to understand the lifecycle of a thread. 

Before we get started: 

The class Thread contains many methods that might affect the life of a thread. They are: 
public void start( );
public void run( );
public void stop( ); // Deprecated, do not use
public void resume( ); // Deprecated, do not use
public void suspend( ); // Deprecated, do not use
public static void sleep(long millis);
public static void sleep(long millis, int nanos);
public boolean isAlive( );
public void interrupt( );
public boolean isInterrupted( );
public static boolean interrupted( );
public void join( ) throws InterruptedException;

Each of these methods has a purpose and an effect on the thread when used. 

Creating a Thread

The first phase in this lifecycle is thread creation. Threads are represented by instances of the Thread class, so creating a thread is done by calling a constructor of that class. In our example, we use the simplest constructor available to us. Additional constructors of the Thread class allow you to specify the thread's name or a Runnable object to serve as the thread's target.

All threads have names that serve to identify them in the virtual machine. By default, that name consists of information about the thread: its priority, its thread group, and other thread information. If you like, you can give a thread a different name, perhaps one that will have meaning to you if you print it out. 

Starting a Thread

A thread exists once it has been constructed, but at that point it is not executing any code. The thread is in a waiting state.

In this waiting state, other threads can interact with the existing thread object. Various attributes of the waiting thread can be set: its priority, its name, its daemon status, and so on. Therefore, even though the thread is waiting, its state may be changed by other threads.
When you're ready for the thread to begin executing code, you call its start() method. This method performs some internal housekeeping and calls the thread's run() method. When the start() method returns, two threads are now executing in parallel: the original thread (which has returned from calling the start() method) and the newly started thread (which is now executing its run() method).

After its start() method has been called, the new thread is said to be alive. In fact, the Thread class has an isAlive() method that tells you the state of the thread: if the isAlive() method returns true, the thread has been started and is executing its run() method. If the isAlive( ) method returns false, however, the thread may not be started yet or may be terminated.

Terminating a Thread

Once started, a thread executes only one method: the run() method. The run() method may be very complicated, it may execute forever, and it may call millions of other methods. Regardless of all this, once the run() method finishes executing, the thread has completed its execution. Like all Java methods, the run() method finishes when it executes a return statement, when it executes the last statement in its method body, or when it throws an exception or fails to catch an exception thrown to it.

As a result, the only way to terminate a thread is to arrange for its run() method to complete. If you look at the documentation of the Thread class, you notice that the class contains a stop() method which seems like it might be used to terminate a thread. It turns out that the stop() method has an inherent problem (an internal race condition, we will see what a race condition is very soon). As a result, the stop() method is deprecated and should not be used. Some Java implementations prohibit its use directly, and the security manager can also be used to prohibit programs from calling it.

There are many threads that you don't need to stop. Often, threads are performing a fixed task, and you always want the task to run to completion. In other cases, the thread can run until the application exits (e.g., when we call the System.exit() method in response to the user pressing the Quit button).

Often, however, you want a thread to continue to execute until some other condition is met. 
The run() method cannot throw a checked exception, but like all Java methods, it can throw an unchecked exception. Throwing an unchecked exception (an exception that extends the RuntimeException class)—or failing to catch a runtime exception thrown by something the run( ) method has called—also causes a thread to stop. 

Pausing, Suspending, and Resuming Threads

Once a thread begins executing its run() method, it continues execution until the run() method completes. If you're familiar with other thread models, you may know of a concept called thread suspension, where a thread is told to pause its execution. Later, the thread is resumed, which is to say that it is told to continue its execution. The Thread class contains suspend() and resume() methods, but they suffer from the same race condition problem as the stop() method, and they, too, are deprecated.

It is possible for a thread to suspend its own execution for a specific period of time by calling the sleep() method. When a thread executes the sleep() method, it pauses for a given number of milliseconds, during which it is said to be asleep. When the pause time has elapsed, the thread wakes up and continues execution with the statements immediately following the sleep() method.

The Thread class provides a version of the sleep() method that allows the developer to specify the time in nanoseconds. Most Java virtual machines do not support this sort of precise timing. When the sleep() method executes, it rounds the nanosecond argument to the nearest millisecond. In fact, most operating systems then further adjust the millisecond argument so that it is a multiple of some number: e.g., 20 or 50 milliseconds. Consequently, the least amount of time that you can sleep on most Java implementations is 20 or 50 milliseconds.
Note that this is true even in J2SE 5.0, which introduces other nanosecond time functionality (e.g., the System.nanoTime() method). The resolution of the sleep( ) method is still only good to a few milliseconds.

Strictly speaking, sleeping is not the same thing as thread suspension. One important difference is that with true thread suspension, one thread would suspend (and later resume) another thread. Conversely, the sleep() method affects only the thread that executes it; it's not possible to tell another thread to go to sleep.

Threads can use the wait and notify mechanism to achieve the functionality of thread suspension and resumption. The difference is that the threads must be coded to use that technique (rather than a generic suspend/resume mechanism that could be imposed from other threads). Don't worry much about the wait and notify just yet. We will cover it in detail very soon. 

Thread Cleanup

A thread that has completed its run() method has terminated. It is no longer active (the isAlive() method returns false). However, the thread object itself may be holding interesting information. As long as some other active object holds a reference to the terminated thread object, other threads can execute methods on the terminated thread and retrieve that information. If the thread object representing the terminated thread goes out of scope, the thread object is garbage collected. On some platforms, this also has the effect of cleaning up system resources associated with the thread.

In general, then, you should not hold onto thread references so that they may be collected when the thread terminates.

One reason to hold onto a thread reference is to determine when it has completed its work. That can be accomplished with the join() method. The join() method is often used when you have started threads to perform discrete tasks and want to know when the tasks have completed. 
The join() method blocks until the thread has completed its run() method. If the thread has already completed its run() method, the join() method returns immediately. This means that you may call the join() method any number of times to see whether a thread has terminated. Be aware, though, that the first time you call the join() method, it blocks until the thread has actually completed. You cannot use the join() method to poll a thread to see if it's running (instead, use the isAlive() method just discussed). 

For Further Reading,
life cycle of a thread, life cycle of threads, lifecycle of a thread, Studies, thread life cycle, thread lifecycle, threads life cycle


Post a Comment


View My Stats