Search This Blog

Showing posts with label Java. Show all posts
Showing posts with label Java. Show all posts

Saturday, March 19, 2011

Introduction to Thread in Java (part 2): Thread Life Cycle

Welcome to the second installment of the series "Introduction to Thread in Java." If you haven't read part 1, here is the link for you. Please read it because we covered a lot of important and fundamental concepts in the first part.

In this section, we'll learn more about the life cycle of threads. Specifically, we need to learn what states threads go through and why / how they end up in a particular state.

Overview

In the simplest form, a thread's life cycle consists of five states:

  1. New state: this is when the threadable / Thread object is first created by calling the constructor of the Thread class. For example, our thread is in new state after this statement Thread newThread = new (runnableObj, threadName);
  2. Runnable / Read-to-run state: after calling the start() method on the thread, it enters the runnable state. That means it is ready to run or to go to some other states. As of the current version of Java, a thread must enter the runnable state before going to any other state.
  3. Running state: a thread is in this state while its run() method is executing. During this state, the thread can go to blocked state if it gets blocked or has to wait for other threads. When the run() method finishes, this state ends.
  4. Blocked/waiting state: while running a thread can be put to sleep, interrupted or blocked by other threads. There are many reasons why this may happen. We'll go deeper in the next section.

  5. Terminated / dead state: when a thread reaches this state, its life ends. It can never be revived again. Normally, a thread enters this state because its run() method has ended. However, a thread can also be terminated even before it is in the runnable state or during the waiting state by calling stop() or suspense() on the thread. I don't suggest using any of those methods to move a thread to its terminated state because those methods have been deprecated by Java and are not thread safe. Therefore, ending the run() method is the best way to go. We'll discuss more on how to do that later.

Here is the UML state diagram that describes the states that a Java thread goes through and how it gets to those states.

Next, let's examine the Blocked/Waiting state and the Terminated/Dead state. They are more complicated than other three states.

Blocked / Waiting state

As we have seen, a thread can only enter this state while it is running. So why does a thread enter this state? There are many causes. The main three are:

  1. Yielding for another thread: current thread yields for another thread to run first. Sometimes, we have a particular thread that needs to run immediately, so we call the yield() method on any running thread to reclaim CPU and resources for the important thread. As the result, any running thread enters the blocked state until the important thread finishes and releases the CPU / resources.
  2. Waiting for another thread: somewhat similar to yielding, there are times when a thread needs to wait for another thread to finish first before it can continue. For instance, if two threads share the same resource, and one of the two is holding that resource, then the other thread needs to wait for that resource to be released.
  3. Waiting for I/O: as you may know, the I/O speed is extremely slow compared to the CPU speed. Thus, if a thread needs some resources / inputs from I/O, it may need to enter the waiting state in order to let the CPU work on other threads. This practice is better for utilizing the CPU because it should be working on other threads / processes instead of siting idle and waiting for a particular thread.

The Java Thread API provides all functions needed to resolve all the situations above. For example, the join() method allows one thread to wait for the completion of another thread. Moreover, you can always cause a thread to enter the waiting state at anytime by calling the method interrupt() on that thread (there are some cases where threads are not interruptable however). Here is the link to the Java Thread API for further reading.

Terminated / Dead State

Once a thread enters this state, it cannot come back to other states. There two distinct ways to terminate the thread, letting the run() method return and calling stop(), suspense() or destroy() method on the running thread. The former is recommended because it ensures thread safe. In fact, Java has already deprecated stop(), suspense() and destroy() method in the new API.

Here is an excellent article that points out why those methods are bad and why stopping a thread through run() is a good practice.

That's all for the second part of this series. Please leave any comment or suggestion because they are always helpful and appreciated :)

Sunday, February 20, 2011

Introduction to Thread in Java

What is Thread?

Thread: light weight process which can access the resource of the process that creates it. Creating a thread is more efficient than creating a new process. Multi-threaded programs utilize the CPU better than single thread programs. Therefore, multi-threading /concurrency is an important feature in Java.

Thread is efficient because multiple threads can share the same resources. However, this also creates problems in communication between threads and the integrity of the resources they share.

In Java, the Thread class and the Runnable interface are used to make classes executable in threads. In order words, if we want our class to be executed in a separate thread, we must either:

  • Subclass the Thread class
  • Or implement the Runnable interface

Defining threadable classes by subclassing the Thread class

We can make our class threadable by subclassing the Thread class like this:

public class ThreadableClass extends Thread
{
  public void run()
  {
    //code to be executed in thread...
  }
}

Next, the only thing that we must do is to override the run() method in our class. The reason is that when the objects of our class are executed as threads, the run() method is called immediately and only code inside this method is executed.

To use our threadable class, we can just instantiate it like any other class. For example:

ThreadableClass newThread = new ThreadableClass();

Defining threadable classes by implementing the Runnable interface

In this method, to create a new thread, we just need to create a new Thread object by calling the Thread constructor. There are several thread constructors. You can find them here Java Thread API. We'll focus only on the most common ones which are Thread(Runnable target) and Thread(Runnable target, String name).

Thread(Runnable target) creates a new thread that contains the "target" runnable object. This means that whenever the thread starts, the object is immediately executed in that thread.

Thread(Runnable target, String name) not only creates a new thread containing the "target" object but it also accepts a name which you give to it.

Here is an example of how to create a thread and give it a name:

Thread newThread = new Thread(myObject, "Thread 1");

Similar to subclassing the Thread class, the class that implements the Runnable interface must also implement the run() method. That method is called when a thread starts. Therefore, any code to be executed in the thread must be inside the run() method. In addition, the thread stops when the run() method returns. It is important to remember that when implementing the run() method.

Here is how to implement the Runnable interface:

public class RunnableClass extends ParentClass implements Runnable
{
  public void run()
  {
    //code to be executed in thread...
  }
}

Notice that our RunnableClass is now a runnable class while still able to inherit methods from its ParentClass.

To use our runnable class, we must instantiate the Thread object and pass in the object of our runnable class as an argument. Whenever the Thread object starts, our object will be executed inside that thread. For instance:

Thread runnableThreadClass = new Thread(runnableClassObject, threadName);

Implement Runnable vs. subclass Tread

So which method is better? In general, implementing the Runnable class is the way to go because it gives our classes better flexibility. For instance, once our classes extend the Thread class, they cannot inherit from any other class because Java doesn't support multiple, direct inheritance.

However, when writing simple applications that utilize threads, subclassing the Thread class is faster and easier to use.

That is it for this first installment. Here is the link to the second post of the series "Thread Life Cycle".