Main Page

Previous Next

Understanding Threads

Many programs, of any size, contain some code segments that are more or less independent of one another, and that may execute more efficiently if the code segments could be overlapped in time. Threads provide a way to do this. Of course, if like most people your computer only has one processor, you can't execute more than one computation at any instant, but you can overlap input/output operations with processing.

Another reason for using threads is to allow processes in a program that need to run continuously, such as a continuously running animation, to be overlapped with other activities in the same program. Java applets in a web page are executed under the control of your browser, and threads make it possible for multiple applets to be executing concurrently. In this case the threads serve to segment the activities running under the control of the browser so that they appear to run concurrently. If you only have one processor, this is an illusion created by your operating system since only one thread can actually be executing instructions at any given instant, but it's a very effective illusion. To produce animation, you typically put some code that draws a succession of still pictures in a loop that runs indefinitely.

The code to draw the picture generally runs under the control of a timer so that it executes at a fixed rate, for example, 20 times per second. Of course, nothing else can happen in the same thread while the loop is running. If you want to have another animation running, it must be in a separate thread. Then the multitasking capability of your operating system can allow the two threads to share the available processor time, thus allowing both animations to run.

Let's get an idea of the principles behind how threads operate. Consider a very simple program that consists of three activities:

  • Reads a number of blocks of data from a file

  • Performs some calculation on each block of data

  • Writes the results of the calculation to another file

You could organize the program as a single sequence of activities. In this case the activities– read file, process, write file– run in sequence, and the sequence is repeated for each block to be read and processed. You could also organize the program so that reading a block from the file is one activity, performing the calculation is a second activity, and writing the results is a third activity. Both of these situations are illustrated below.

Click To expand

Once a block of data has been read, the computation process can start, and as soon as the computation has been completed, the results can be written out. With the program executing each step in sequence (that is, as a single thread), as shown in the top half of the diagram, the total time for execution will be the sum of the times for each of the individual activities. However, suppose we were able to execute each of the activities independently, as illustrated in the lower half of the diagram. In this case, reading the second block of data can start as soon as the first block has been read, and in theory we can have all three activities executing concurrently. This is possible even though you only have one processor, because the input and output operations are likely to require relatively little processor time while they are executing, so the processor can be doing other things while they are in progress. This can have the effect of reducing the total execution time for the program.

These three processes that we have identified that run more or less independently of one another – one to read the file, another to process the data, and a third to write the results – are called threads. Of course, the first example at the top of the diagram has just one thread that does everything in sequence. Every Java program has at least one thread. However, the three threads in the lower example aren't completely independent of one another. After all, if they were, you might as well make them independent programs. There are practical limitations too – the potential for overlapping these threads will be dependent on the capabilities of your computer, and of your operating system. However, if you can get some overlap in the execution of the threads, the program is going to run faster. There's no magic in using threads, though. Your computer has only a finite capacity for executing instructions, and if you have many threads running you may in fact increase the overall execution time because of the overhead implicit in managing the switching of control between threads.

An important consideration when you have a single program running as multiple threads is that the threads are unlikely to have identical execution times, and, if one thread is dependent on another, you can't afford to have one overtaking the other – otherwise you'll have chaos. Before you can start calculating in the example in the diagram, you need to be sure that the data block the calculation uses has been read, and before you can write the output, you need to know that the calculation is complete. This necessitates having some means for the threads to communicate with one another.

The way we have shown the threads executing in the previous diagram isn't the only way of organizing the program. You could have three threads, each of which reads the file, calculates the results, and writes the output, as shown here.

Click To expand

Now there's a different sort of contention between the threads. They are all competing to read the file and write the results, so there needs to be some way of preventing one thread from getting at the input file while another thread is already reading from it. The same goes for the output file. There's another aspect of this arrangement that is different from the previous version. If one thread, thread1 say, reads a block, block4 perhaps, that needs a lot of time to compute the results, another thread, thread2 say, could conceivably read a following block, block5 maybe, and calculate and write the results for block5 before thread1 has written the results for block4. If you don't want the results appearing in a different sequence from the input, you should do something about this. Before we delve into the intricacies of making sure our threads don't get knotted, let's first look at how we create a thread.

Creating Threads

Your program always has at least one thread: the one created when the program begins execution. With a program, this thread starts at the beginning of main(). With an applet, the browser is the main thread. That means that when your program creates a thread, it is in addition to the main thread of execution that created it. As you might have guessed, creating an additional thread involves using an object of a class, and the class you use is java.lang.Thread. Each additional thread that your program creates is represented by an object of the class Thread, or of a subclass of Thread. If your program is to have three additional threads, you will need to create three such objects.

To start the execution of a thread, you call the start() method for the Thread object. The code that executes in a new thread is always a method called run(), which is public, accepts no arguments, and doesn't return a value. Threads other than the main thread in a program always start in the run()method for the object that represents the thread. A program that creates three threads is illustrated diagrammatically here:

Click To expand

For a class representing a thread in your program to do anything, you must implement the run() method as the version defined in the Thread class does nothing. Your implementation of run() can call any other methods you want. Our illustration shows main() creating all three threads, but that doesn't have to be the case. Any thread can create more threads.

Now here comes the bite; you don't call the run() method to start a thread, you call the start() method for the object representing the thread and that causes the run() method to be called. When you want to stop the execution of a thread that is running, you call the stop() method for the Thread object.

The reason for this is somewhat complex but basically boils down to this: Threads are always owned and managed by the operating system and a new thread can only be created and started by the operating system. If you were to call the run() method yourself, it would simply operate like any other method, running in the same thread as the program that calls it.

When you call the start() method for a Thread object, you are calling a native code method that causes the operating system to initiate another thread from which the run() method for the Thread object executes.

In any case, it is not important to understand exactly how this works. Just remember: always start your thread by calling the start() method. If you try to call the run() method directly yourself, then you will not have created a new thread and your program will not work as you intended.

There are two ways in which you can define a class that is to represent a thread. One way is to define your class as a subclass of Thread and provide a definition of the method run() that overrides the inherited method. The other possibility is to define your class as implementing the interface Runnable, which declares the method run(), and then create a Thread object in your class when you need it. We will look at and explore the advantages of each approach in a little more detail.

Try It Out – Deriving a Subclass of Thread

We can see how this works by using an example. We'll define a single class, TryThread, which we'll derive from Thread. Execution starts in the method main():

import java.io.IOException;

public class TryThread extends Thread {
  private String firstName;          // Store for first name
  private String secondName;         // Store for second name
  private long aWhile;               // Delay in milliseconds

  public TryThread(String firstName, String secondName, long delay) {
    this.firstName = firstName;      // Store the first name
    this.secondName = secondName;    // Store the second name
    aWhile = delay;                  // Store the delay
    setDaemon(true);                 // Thread is daemon
  }

  public static void main(String[] args) {
    // Create three threads
    Thread first = new TryThread("Hopalong ", "Cassidy ", 200L);
    Thread second = new TryThread("Marilyn ", "Monroe ", 300L);
    Thread third = new TryThread("Slim ", "Pickens ", 500L);

    System.out.println("Press Enter when you have had enough...\n");
    first.start();                      // Start the first thread
    second.start();                     // Start the second thread
    third.start();                      // Start the third thread

    try {
      System.in.read();                 // Wait until Enter key pressed
      System.out.println("Enter pressed...\n");

    } catch (IOException e) {           // Handle IO exception
      System.out.println(e);            // Output the exception
    }
    System.out.println("Ending main()");
    return;
  }

  // Method where thread execution will start
  public void run() {
    try {
      while(true) {                          // Loop indefinitely...
        System.out.print(firstName);         // Output first name
        sleep(aWhile);                       // Wait aWhile msec.
        System.out.print(secondName + "\n"); // Output second name
      }
    } catch(InterruptedException e) {        // Handle thread interruption
      System.out.println(firstName + secondName + e);     // Output the exception
    }
  }
}

If you compile and run the code, you'll see something like this:

Press Enter when you have had enough...

Hopalong Marilyn Slim Cassidy
Hopalong Monroe
Marilyn Cassidy
Hopalong Pickens
Slim Monroe
Marilyn Cassidy
Hopalong Cassidy
Hopalong Monroe
Marilyn Pickens
Slim Cassidy
Hopalong Monroe
Marilyn Cassidy
Hopalong Cassidy
Hopalong Monroe
Marilyn Pickens
Slim Cassidy
Hopalong Cassidy
Hopalong Monroe
Marilyn
Enter pressed...

Ending main()

How It Works

There are three instance variables in our class TryThread and these are initialized in the constructor. The two String variables hold first and second names, and the variable aWhile stores a time period in milliseconds. The constructor for our class, TryThread(), will automatically call the default constructor, Thread(), for the base class.

Our class containing the method main() is derived from Thread, and implements run(), so objects of this class represent threads. The fact that each object of our class will have access to the method main() is irrelevant – the objects are perfectly good threads. Our method main() creates three such objects: first, second, and third.

Daemon and User Threads

The call to setDaemon(), with the argument true in the TryThread constructor, makes the thread that is created a daemon thread. A daemon thread is simply a background thread that is subordinate to the thread that creates it, so when the thread that created the daemon thread ends, the daemon thread dies with it. In our case, the method main() creates the daemon threads so that when main() returns, all the threads it has created will also end. If you run the example a few times pressing Enter at random, you should see that the daemon threads die after the main() method returns, because, from time to time, you will get some output from one or other thread after the last output from main().

A thread that isn't a daemon thread is called a user thread. The diagram below shows two daemon threads and a user thread that are created by the main thread of a program.

Click To expand

A user thread has a life of its own that is not dependent on the thread that creates it. It can continue execution after the thread that created it has ended. The default thread that contains main() is a user thread, as shown in the diagram, but thread3 shown in the diagram could continue to execute after main() has returned. Threads that run for a finite time are typically user threads, but there's no reason why a daemon thread can't be finite. Threads that run indefinitely should usually be defined as daemon threads simply because you need a means of stopping them. A hypothetical example might help you to understand this so let's consider how a network server handling transactions of some kind might work in principle.

A network server might be managed overall by a user thread that starts one or more daemon threads to listen for requests. When the server starts up, the operator starts the management thread and this thread creates daemon threads to listen for requests. Each request recognized by one of these daemon threads might be handled by another thread that is created by the listening thread, so that each request will be handled independently. Where processing a transaction takes a finite time, and where it is important that the requests are completed before the system shuts down, the thread handling the request might be created as a user thread to ensure that it runs to completion, even if the listening thread that created it stops.

When the time comes to shut the system down, the operator doesn't have to worry about how many listening threads are running. When the main thread is shut down, all the listening threads will also shut down because they are daemon threads. Any outstanding threads dealing with specific transactions will then run to completion.

Note that you can only call setDaemon() for a thread before it starts; if you try to do so afterwards, the method will throw an IllegalThreadStateException exception. Also, a thread that is itself created by a daemon thread will be a daemon by default.

Creating Thread Objects

In the method main(), we create three Thread variables that store three different objects of our class TryThread. As you can see, each object has an individual name pair as the first two arguments to its constructor, and a different delay value passed as the third argument. All objects of the class TryThread are daemon threads because we call setDaemon() with the argument true in the constructor. Since the output can continue indefinitely, we display a message to explain how to stop it.

Once you've created a thread, it doesn't start executing by itself. You need to set it going. As we said earlier, you don't call the run() method for the Thread object to do this, you call its start() method. Thus we start the execution of each of the threads represented by the objects first, second, and third by calling the start() method that is inherited from Thread for each object. The start() method starts the object's run() method executing, then returns to the calling thread. Eventually all three threads are executing in parallel with the original application thread, main().

Implementing the run() Method

The run() method contains the code for the thread execution. The code in this case is a single, infinite while loop which we put in a try block because the sleep()method that is called in the loop can throw the InterruptedException exception caught by the catch block. The code in the loop outputs the first name, calls the method sleep() inherited from Thread, and then outputs the second name. The sleep() method suspends execution of the thread for the number of milliseconds that you specify in the argument. This gives any other threads that have previously been started a chance to execute. This allows the output from the three threads to become a little jumbled.

Each time a thread calls the method sleep(), one of the other waiting threads jumps in. You can see the sequence in which the threads execute from the output. From the names in the output you can deduce that they execute in the sequence first, second, third, first, first, second, second, first, first, third, and so on. The actual sequence depends on your operating system scheduler so this is likely to vary from machine to machine. The execution of the read() method that is called in main() is blocked until you press Enter, but all the while the other threads continue executing. The output stops when you press Enter because this allows the main thread to continue and execute the return. Executing return ends the thread for main(), and since the other threads are daemon threads they also die when the thread that created them dies, although as you may have seen, they can run on a little after the last output from main().

Stopping a Thread

If we did not create the threads in the last example as daemon threads, they would continue executing independently of main(). If you are prepared to terminate the program yourself (use Ctrl+C in a DOS session running Java) you can demonstrate this by commenting out the call to setDaemon() in the constructor. Pressing Enter will end main(), but the other threads will continue indefinitely.

A thread can signal another thread that it should stop executing by calling the interrupt() method for that Thread object. This in itself doesn't stop the thread, it just sets a flag in the thread that indicates an interruption has been requested. This flag must be checked in the run() method to have any effect. As it happens the sleep() method checks whether the thread has been interrupted, and throws an InterruptedException if it has been. You can see that in action by altering the previous example a little.

Try It Out – Interrupting a Thread

Make sure the call to the setDaemon() method is still commented out in the constructor, and modify the main()method as follows:

  public static void main(String[] args) {
    // Create three threads
    Thread first = new TryThread("Hopalong ", "Cassidy ", 200L);
    Thread second = new TryThread("Marilyn ", "Monroe ", 300L);
    Thread third = new TryThread("Slim ", "Pickens ", 500L);

    System.out.println("Press Enter when you have had enough...\n");
    first.start();                      // Start the first thread
    second.start();                     // Start the second thread
    third.start();                      // Start the third thread
    try {
      System.in.read();                 // Wait until Enter key pressed
      System.out.println("Enter pressed...\n");

      // Interrupt the threads
      first.interrupt();
      second.interrupt();
      third.interrupt();

    } catch (IOException e) {           // Handle IO exception
      System.out.println(e);            // Output the exception
    }
    System.out.println("Ending main()");
    return;
  }

Now the program will produce output that is something like:

Press Enter when you have had enough...

Slim Hopalong Marilyn Cassidy
Hopalong Monroe
Marilyn Cassidy
Hopalong Pickens
Slim Cassidy
Hopalong Monroe
Marilyn
Enter pressed...

Ending main()
Marilyn Monroe java.lang.InterruptedException: sleep interrupted
Slim Pickens java.lang.InterruptedException: sleep interrupted
Hopalong Cassidy java.lang.InterruptedException: sleep interrupted

How It Works

Since the method main() calls the interrupt() method for each of the threads after you press the Enter key, the sleep() method called in each thread registers the fact that the thread has been interrupted and throws an InterruptedException. This is caught by the catch block in the run() method and produces the new output you see. Because the catch block is outside the while loop, the run() method for each thread returns and each thread terminates.

You can check whether a thread has been interrupted by calling the isInterrupted()method for the thread. This returns true if interrupt() has been called for the thread in question. Since this is an instance method, you can use this to determine in one thread whether another thread has been interrupted. For example, in main() you could write:

if(first.isInterrupted())
  System.out.println("First thread has been interrupted.");

Note that this only determines whether the interrupted flag has been set by a call to interrupt() for the thread – it does not determine whether the thread is still running. A thread could have its interrupt flag set and continue executing – it is not obliged to terminate because interrupt() is called. To test whether a thread is still operating you can call its isAlive() method. This returns true if the thread has not terminated.

The instance method isInterrupted() has no effect on the interrupt flag in the thread – if it was set, it remains set. However, the static method interrupted() in the Thread class is different. It tests whether the currently executing thread has been interrupted and, if it has, it clears the interrupted flag in the current Thread object and returns true.

When an InterruptedException is thrown, the flag that registers the interrupt in the thread is cleared, so a subsequent call to isInterrupted() or interrupted() will return false.

Connecting Threads

If you need to wait in one thread until another thread dies, you can call the join() method for the thread that you expect isn't long for this world. Calling the join() method with no arguments will halt the current thread for as long as it takes the specified thread to die:

thread1.join();     // Suspend the current thread until thread1 dies

You can also pass a long value to the join() method to specify the number of milliseconds you're prepared to wait for the death of a thread:

thread1.join(1000); // Wait up to 1 second for thread1 to die

If this is not precise enough, there is a version of join() with two parameters. The first is a time in milliseconds and the second is a time in nanoseconds. The current thread will wait for the duration specified by the sum of the arguments. Of course, whether or not you get nanosecond resolution will depend on the capability of your hardware.

The join() method can throw an InterruptedException if the current thread is interrupted by another thread, so you should put a call to join() in a try block and catch the exception.

Thread Scheduling

The scheduling of threads depends to some extent on your operating system, but each thread will certainly get a chance to execute while the others are 'asleep', that is, when they've called their sleep() methods. If your operating system uses preemptive multitasking, as Windows 98 does, the program will work without the call to sleep() in the run() method (you should also remove the try and catch blocks, if you remove the sleep() call). However, if your operating system doesn't schedule in this way, without the sleep() call in run(), the first thread will hog the processor, and will continue indefinitely.

The diagram below illustrates how four threads might share the processor over time by calling the sleep() method to relinquish control.

Click To expand

Note that there's another method, yield(), defined in the Thread class, that gives other threads a chance to execute. You would use this when you just want to allow other threads a look-in if they are waiting, but you don't want to suspend execution of the current thread for a specific period of time. When you call the sleep() method for a thread, the thread will not continue for at least the time you have specified as an argument, even if no other threads are waiting. Calling yield() on the other hand will cause the current thread to resume immediately if no threads are waiting.

Implementing the Runnable Interface

As an alternative to defining a new subclass of Thread, we can implement the interface Runnable in a class. You'll find that this is generally much more convenient than deriving a class from Thread, because you can derive your class from a class other than Thread, and it can still represent a thread. Because Java only allows a single base class, if you derive your class from Thread, it can't inherit functionality from any other class. The interface Runnable only declares one method, run(), and this is the method that will be executed when the thread is started.

Try It Out – Using the Runnable Interface

To see how this works in practice, we can write another version of the previous example. We'll call this version of the program JumbleNames:

import java.io.IOException;

public class JumbleNames implements Runnable {
  private String firstName;                         // Store for first name
  private String secondName;                        // Store for second name
  private long aWhile;                              // Delay in milliseconds

  // Constructor
  public JumbleNames(String firstName, String secondName, long delay) {
    this.firstName = firstName;                     // Store the first name
    this.secondName = secondName;                   // Store the second name
    aWhile = delay;                                 // Store the delay
  }

  // Method where thread execution will start
  public void run() {
    try {
      while(true) {                                 // Loop indefinitely...
        System.out.print(firstName);                // Output first name
        Thread.sleep(aWhile);                       // Wait aWhile msec.
        System.out.print(secondName+"\n");          // Output second name
      }
    } catch(InterruptedException e) {               // Handle thread interruption
      System.out.println(firstName + secondName + e);     // Output the exception
    }
  }

  public static void main(String[] args) {
    // Create three threads
    Thread first = new Thread(new JumbleNames("Hopalong ", "Cassidy ", 200L));
    Thread second = new Thread(new JumbleNames("Marilyn ", "Monroe ", 300L));
    Thread third = new Thread(new JumbleNames("Slim ", "Pickens ", 500L));

    // Set threads as daemon
    first.setDaemon(true);
    second.setDaemon(true);
    third.setDaemon(true);
    System.out.println("Press Enter when you have had enough...\n");
    first.start();                                 // Start the first thread
    second.start();                                // Start the second thread
    third.start();                                 // Start the third thread
    try {
      System.in.read();                            // Wait until Enter key pressed
      System.out.println("Enter pressed...\n");

    } catch (IOException e) {                      // Handle IO exception
      System.out.println(e);                       // Output the exception
    }
    System.out.println("Ending main()");
    return;
  }
}

How It Works

We have the same data members in this class as we had in the previous example. The constructor is almost the same as previously. We can't call setDaemon() in this class constructor because our class isn't derived from Thread. Instead, we need to do that in main() after we've created the objects representing the threads. The run() method implementation is also very similar. Our class doesn't have sleep() as a member, but because it's a public static member of the class Thread, we can call it in our run() method by using the class name.

In the method main(), we still create a Thread object for each thread of execution, but this time we use a constructor that accepts an object of type Runnable as an argument. We pass an object of our class JumbleNames to it. This is possible because our class implements Runnable.

Thread Names

Threads have a name, which in the case of the Thread constructor we're using in the example, will be a default name composed of the string "Thread*" with a sequence number appended. If you want to choose your own name for a thread, you can use a Thread constructor that accepts a String object specifying the name you want to assign to the thread. For example, we could have created the Thread object first with the statement:

Thread first = new Thread(new JumbleNames ("Hopalong ", "Cassidy ", 200L),
                          "firstThread");

This assigns the name "firstThread" to the thread. Note that this name is only used when displaying information about the thread. It has no relation to the identifier for the Thread object, and there's nothing, apart from common sense, to prevent several threads being given the same name.

You can obtain the name assigned to a thread by calling the getName() method for the Thread object. The name of the thread is returned as a String object. You can also change the name of a thread by calling the setName() method defined in the class Thread and passing a String object to it.

Once we've created the three Thread objects in the example, we call the setDaemon()method for each. The rest of main() is the same as in the original version of the previous example, and you should get similar output when you run this version of the program.

Previous Next
JavaScript Editor Java Tutorials Free JavaScript Editor