Threading Techniques

This chapter will answer the following questions:


Overview

Whether you are aware of it or not, threads are a part of every Java program; when you run a Java application, the Java VM runs its main() method inside its own thread -applets are also run in their own threads. This fact proves how integral the concept of threads is to Java. To become a powerful Java programmer, you need to know how threads work and how they can be used in the development of Java programs.

Why Are Threads Useful?

First of all, a thread is a single sequence of execution that can run independently within an application. A multi-threaded application is one, which supports the concurrent execution of multiple threads. To understand why threads are useful, we'll consider an example of how using threads in an application can be very productive, while not using them can be counter-productive. Consider the case where you want to use a web browser to download a file from one site and access another site at the same time. If your browser does not allow you to do these two things, simultaneously, you are forced to wait for the file to finish downloading before you can do anything else-and if that file is relatively large, you can imagine how annoying that can be. It certainly would be much more productive to take advantage of the time the browser is spending downloading the file to do other things. Luckily, most web browsers are multi-threaded.

Why Haven't I Heard of Threads Before?

The concept of threads has actually been around for a long time. Threads were not as popular as they are now because, prior to Java, no API managed to standardize the use of threads across different platforms. Java came along with a threading API that is supported by all JVMs running on all platforms.


Creating a Thread

It's now time to show you how a thread is created in Java. The java.lang package defines a Thread class, which is used to create threads. So to create a thread in your application you could have a declaration similar to the following:

Thread myFirstThread = new Thread();

This declaration creates an instance of Thread called myFirstThread; however, it is not a particularly useful declaration. The reason is that there is no way you can tell myFirstThread to do what you want it to do. Thread defines methods that define the general behavior of all threads; it has no methods or properties that can perform the particular tasks you might have intended for it. Does this mean that the Thread class is useless? No, it simply means that we have not yet seen how to make good use of it. Be patient and read on.

There are two ways used to create threads: the first involves subclassing the Thread class, and the second involves implementing the Runnable interface. We will look at both ways next.

Subclassing the Thread Class

It was stated in the previous section that an object of type Thread cannot be directly programmed by the user to perform a particular task. If that's the case, how can any thread be programmed? The answer lies in one of the methods that the Thread class defines:

public void run() {}

This method is where the thread's main execution logic lies; the run() method is to a thread what the main() method is to an application. Just as, main() is the starting point of execution for an application, run() is the first thing that executes when a thread is started.

The default run() method basically does nothing; directly instantiating the Thread class, therefore, results in a thread that has a useless run() method. The obvious solution to this is to subclass Thread and override its run() method.

Here's an example of how this is done:

public class myUsefulThread extends Thread {
  //constructors
  public void run() {
    //do something useful
  }
}

Now, when you instantiate myUsefulThread, its object will get its functionality from the overridden run() method.

Lastly, you need to know how to start your thread. To start a thread, simply call its start() method, as in the following:

myUsefulThread t = new myUsefulThread();
t.start();

Example: Implementing countingThread

countingThread is a simple thread class that counts from one number to another. While this is a simplified example, it nevertheless illustrates how separate threads are created and how they can co-exist in the same environment. countingThread is shown next:

public class countingThread extends Thread {
 private int start;
 private int finish;

 public countingThread(int from, int to) {
  this.start = from;
  this.finish = to;
 }

 public void run() {
   System.out.println(this.getName()+ " started executing...");
   for (int i = start; i <= finish; i++) {
     System.out.print (i + " ");
   } //for
   System.out.println(this.getName() + " finished executing.");
 } //run
}

countingThread's constructor takes two int parameters and uses them to initialize its properties start and finish. The run() method first identifies the current thread object by printing its name to the screen. It then uses a for loop to count from start to finish, and print each number to the screen. Before it is done, it prints a string stating that the current thread has finished executing.

We can use countingThread in an application as follows:

public class threadTester {
  static public void main(String[] args) {
    countingThread thread1 = new countingThread (1, 10);
    countingThread thread2 = new countingThread (20, 30);
    thread1.start();
    thread2.start();
  }
}

The above main() method creates two countingThread objects: thread1 will count from 1 to 10, and thread2 will count from 20 to 30. It then starts both threads by calling their start() methods. When this main() method is run in an application, the output could be similar to the following:

Thread-1 started executing...
Thread-2 started executing...
20 21 22 23 24 25 26 27 28 29 30 Thread-2 finished executing.
1 2 3 4 5 6 7 8 9 10 Thread-1 finished executing.

First, notice that the output does not show the threads' names as thread1 and thread2, as you might have expected. Unless you specifically assign a name to a thread, Java will automatically give it a name of the form Thread-n, where n is a unique number. In this case, the first thread to start was given the name Thread-1, and the second the name Thread-2. If you prefer your own, more descriptive names, use Thread's setName(String) method.

Next, notice that while Thread-1 started executing first, it finished last. What's going on here? The answer is that threads in Java are not guaranteed to execute in any particular sequence. In fact, each time you execute threadTester, you may get a different output. Here's another output generated by threadTester:

Thread-1 started executing...
1 2 3 4 5 6 7 8 Thread-2 started executing...
20 21 22 23 9 10 Thread-1 finished executing.
24 25 26 27 28 29 30 Thread-2 finished executing.

Notice how in this output, Thread-2 started executing before Thread-1 was even finished. Basically, the process of scheduling threads is controlled by the Java thread scheduler, and not the programmer.

Implementing the Runnable Interface

As we saw in the previous section, subclassing the Thread class is one way of creating thread objects. That's fine if you are designing a new class whose objects you want to execute in separate threads. But what if you wanted objects of a preexisting class to execute in their own threads? You would just implement the Runnable interface.

The Runnable interface is used to add threading support to classes that do not inherit from the Thread class. It declares only one method:

public void run() {
}

So, in order to have objects of a preexisting class execute in their own threads, you have to do the following:

  1. Make the class implement the Runnable interface
  2. Implement the run() method for that class

To make countingThread2, use the Runnable interface instead of subclassing the Thread class, we only need to change its declaration to the following:

public class countingThread2 implements Runnable {
  //the rest is not changed
}

While the implementation of the countingThread2 class did not have to change to make it implement Runnable, the way its objects are created does have to change. Instead of instantiating countingThread, as we did previously, we now must create a Runnable object from countingThread2 and pass it to one of Thread's constructors. Here's how the thread1 object is now created:

Runnable rThread = new countingThread2(1, 10);
Thread thread1 = new Thread(rThread);

Let's think about why we had to do this. countingThread was subclassed from the Thread class, so it inherited all the behavior of a Java thread. That means that you can perform all the functionality that Thread supports on an object of countingThread, such as calling start(). countingThread2, however, did not inherit anything from Thread, so it did not support any of Thread's behavior. countingThread2 only defined the run() method, which would be called when a countingThread2 object is run in a thread.


The Thread API

Constructors

The Thread class defines the following constructors:

public Thread()
public Thread(Runnable target)
public Thread(Runnable target, String name)
public Thread(String name)
public Thread(ThreadGroup group, Runnable target)
public Thread(ThreadGroup group, String name)
public Thread(ThreadGroup group, Runnable target, String name)

There are only three types of parameters used to construct Thread objects:

The ThreadGroup class is mainly used to organize groups of related threads. A Web browser, for example, could set up a ThreadGroup for all the threads of an applet.

The start() and stop() Methods

public void start()
public final void stop()
public void destroy()

To start a thread, simply call its start() method. If you try to call a thread's start() method more than once, an exception will be thrown. To stop it, you can either use stop() or destroy(). Stop() terminates the thread and throws a ThreadDeath exception to the thread. This is necessary because the thread might be doing something that, if interrupted, may cause unpredictable results; for example, it could be in the middle of modifying a file or some other system resource. To avoid problems of this nature, the ThreadDeath exception should be caught in order to perform any necessary cleanup before the thread terminates.

Be careful when catching the ThreadDeath exception. When stop() throws a ThreadDeath exception and you catch it, you must rethrow it in the catch block. If you do not, the thread will never stop!

Another way to stop a thread is to call its destroy() method. This method, although not currently implemented in the JDK 1.1, does not throw a ThreadDeath exception when called.

The suspend() and resume() Methods

The following methods are used to suspend and resume a thread:

public final void suspend()
public final void resume()

Calling suspend() on a thread temporarily stops it from executing. Calling resume() on it, resumes its execution (unless something else caused the thread to die).

The sleep() Method

Use one of the following two methods to put a thread to sleep:

public static void sleep(long millisecond)
public static void sleep(long millisecond, int nanosecond)

If, for example, you want to cause a thread to sleep for a full second, call sleep(1000) on the thread.

The yield() Method

In some cases, a large number of threads could be waiting on the currently running thread to finish executing before they can start executing. To make the thread scheduler switch from the current running thread to allow others to execute, call the yield() method on the current thread. In order for yield() to work, there must be at least one thread with an equal or higher priority than the current thread.

The join() Method

public final void join()
public final void join(long millisecond)

To a have a thread wait on an executing thread to finish, call the join() method on the thread being waited on. You can specify a timeout for waiting on the thread by passing a time parameter to join() (in milliseconds). join() waits on the thread until either the timeout has expired, or the thread has terminated. To determine whether a thread has terminated, join() uses another method, called isAlive(). If isAlive() returns true, join() does not return and keeps waiting on the thread. If isAlive() returns false, join() returns and the waiting thread can start executing.


A Thread's Lifecycle

A thread goes through several states from the point it starts to the point it ends. When you first create a thread object, it is in the NEW state. When you call the thread's start() method, it becomes Runnable. Remember that a thread does not necessarily start executing immediately after its start() method is called-as we saw in the threadTester application. When in its Runnable state, the thread can become Not Runnable or Dead. Calling sleep(), suspend(), or wait() makes a thread Not Runnable, while calling destroy() or stop() makes it Dead (a thread also becomes Dead when its run() method ends). A thread can be made Runnable again by calling its resume() method, or when sleep() runs out; but a Dead thread can never be revived. There are other conditions that affect a thread's lifecycle; for example, a Runnable thread becomes Not Runnable if it has to wait for a particular operation to end before it can execute.


Making Your Code Thread-Safe

In this section, we will examine why it is important to protect certain code against being executed by multiple threads at the same time, and how you can protect it. At the end of the section, we will discuss Java monitors and their role in multi-threaded programs.

The synchronized Keyword

In some cases, it is necessary to have a method or block of code executed by only one thread at a time. The synchronized keyword is used to achieve that. Before we discuss how synchronized is used, we need to discuss when it needs to be used.

To better understand why using the synchronized keyword is necessary in some cases, consider the following code:

public class Swapper {
  int from;
  int to;
  
  public void swap() {
    int temp = from;
    from = to;
    to = temp;
  }
}

This class declares a swap() method which swaps the values of its two properties, from and to. The method takes the from property, stores it in a temp variable, then it stores the value of to in from and the value of temp in to. Consider the case in which two different methods start their own threads to execute swap() at the same time -we'll call these threads thread1 and thread2. Now assume that thread1 executed the method first, and at the time it began the execution, the values of from and to where 5 and 6, respectively. thread1 executes the first line of code and assigns 5 to temp. At the end of the second line, from is assigned the value 6. At this point, thread2 starts executing the method. To thread2, the value of from is 6 (as assigned by thread1) and to is also 6. thread2 executes the entire method, swapping the values 6 and 6. Once thread2 is done, thread1 resumes its execution at the third line and assigns the value of temp (now 6) to to. When thread1 is done, both from and to have the value 6, and the intended swapping operation (from 5 to 6) never took place!

To prevent the above scenario from ever taking place, we simply add the synchronized keyword to the signature of swap(), as follows:

public synchronized void swap()

Now, only one thread can execute swap() at a time. In this case, thread1 executes the entire method, properly swapping the values of from and to. Once thread1 is finished its execution, thread2 executes swap(), and swaps the values back again.

As a rule of thumb, any method that modifies an object's properties should be declared synchronized.

Monitors

An environment that supports multiple threads must implement some type of concurrency-control technique. There are several such techniques, including semaphores, critical sections, and database record locking. Java implements a concurrency-control mechanism known as a monitor.

Traditionally, a monitor is an object, which monitors something, such as a procedure. The monitor's job is to make sure that only one thread at a time can execute the procedure it is monitoring. In Java, every object has a monitor object associated with it. The monitor makes sure that only one thread at a time is executing any synchronized methods belonging to the object it is monitoring. The monitor blocks any other threads from executing the same synchronized method, until the currently-executing thread is done. If no threads are executing the target synchronized method, a thread can enter the monitor (or lock the monitor for itself). At that point, no other thread can execute the method until the current thread leaves the monitor.

Java monitors are not true objects, in the sense that they do not have methods and properties. They are built into Java's implementation of multithreading and are not visible to the programmer. For that reason, our discussion of monitors has been purely conceptual.


Summary

What was covered in this chapter: