Thread Objects






Thread Objects

Each thread is associated with an instance of the class THRead.[2] There are two basic strategies for using Thread objects to create a concurrent application:

[2] docs/api/java/lang/Thread.html

  • To directly control thread creation and management, simply instantiate Thread each time the application needs to initiate an asynchronous task.

  • To abstract thread management from the rest of your application, pass the application's tasks to an executor.

This section documents the use of Thread objects. Executors are discussed with other high-level concurrency objects (page 395).

Defining and Starting a Thread

An application that creates an instance of Thread must provide the code that will run in that thread. There are two ways to do this:

  • Provide a Runnable object. The Runnable interface[3] defines a single method, run, meant to contain the code executed in the thread. The Runnable object is passed to the Thread constructor, as in the HelloRunnable[4] example:

    [3] docs/api/java/lang/Runnable.html

    [4] tutorial/essential/concurrency/example/HelloRunnable.java

    public class HelloRunnable implements Runnable {
    
      public void run() {
        System.out.println("Hello from a thread!");
      }
    
      public static void main(String args[]) {
        (new Thread(new HelloRunnable())).start();
      }
    }
    
  • Subclass Thread. The THRead class itself implements Runnable, though its run method does nothing. An application can subclass Thread, providing its own implementation of run, as in the HelloThread[5] example:

    [5] tutorial/essential/concurrency/example/HelloThread.java

public class HelloThread extends Thread {

  public void run() {
    System.out.println("Hello from a thread!");
  }

  public static void main(String args[]) {
    (new HelloThread()).start();
  }
}

Notice that both examples invoke Thread.start in order to start the new thread.

Which of these idioms should you use? The first idiom, which employs a Runnable object, is more general, because the Runnable object can subclass a class other than THRead. The second idiom is easier to use in simple applications, but is limited by the fact that your task class must be a descendant of THRead. This chapter focuses on the first approach, which separates the Runnable task from the THRead object that executes the task. Not only is this approach more flexible, but it is applicable to the high-level thread management APIs covered later.

The Thread class defines a number of methods useful for thread management. These include static methods, which provide information about, or affect the status of, the thread invoking the method. The other methods are invoked from other threads involved in managing the thread and Thread object. We'll examine some of these methods in the following sections.

Pausing Execution with sleep

Thread.sleep causes the current thread to suspend execution for a specified period. This is an efficient means of making processor time available to the other threads of an application or other applications that might be running on a computer system. The sleep method can also be used for pacing, as shown in the example that follows, and waiting for another thread with duties that are understood to have time requirements, as with the SimpleThreads example in a later section.

Two overloaded versions of sleep are provided: one that specifies the sleep time to the millisecond and one that specifies the sleep time to the nanosecond. However, these sleep times are not guaranteed to be precise, because they are limited by the facilities provided by the underlying OS. Also, the sleep period can be terminated by interrupts, as we'll see in a later section. In any case, you cannot assume that invoking sleep will suspend the thread for precisely the time period specified.

The SleepMessages[6] example uses sleep to print messages at four-second intervals:

[6] tutorial/essential/concurrency/example/SleepMessages.java

public class SleepMessages {

  public static void main(String args[])
                       throws InterruptedException {
    String importantInfo[] = {
      "Mares eat oats",
      "Does eat oats",
      "Little lambs eat ivy",
      "A kid will eat ivy too"
    };

    for (int i = 0; i < importantInfo.length; i++) {
      // Pause for 4 seconds
      Thread.sleep(4000);
      // Print a message
      System.out.println(importantInfo[i]);
    }
  }
}

Notice that main declares that it throws InterruptedException. This is an exception that sleep tHRows when another thread interrupts the current thread while sleep is active. Since this application has not defined another thread to cause the interrupt, it doesn't bother to catch InterruptedException.

Interrupts

An interrupt is an indication to a thread that it should stop what it is doing and do something else. It's up to the programmer to decide exactly how a thread responds to an interrupt, but it is very common for the thread to terminate. This is the usage emphasized in this chapter.

A thread sends an interupt by invoking interrupt on the THRead object for the thread to be interrupted. For the interrupt mechanism to work correctly, the interrupted thread must support its own interruption.

Supporting Interruption

How does a thread support its own interruption? This depends on what it's currently doing. If the thread is frequently invoking methods that throw InterruptedException, it simply returns from the run method after it catches that exception. For example, suppose the central message loop in the SleepMessages example were in the run method of a thread's Runnable object. Then it might be modified as follows to support interrupts:

for (int i = 0; i < ImportantInfo.length; i++) {
  // Pause for 4 seconds
  try {
      Thread.sleep(4000);
  } catch (InterruptedException e) {
      // We've been interrupted: no more messages.
      return;
  }
  // Print a message
  System.out.println(ImportantInfo[i]);
}

Many methods that throw InterruptedException, such as sleep, are designed to cancel their current operation and return immediately when an interrupt is received.

What if a thread goes a long time without invoking a method that throws InterruptedException? Then it must periodically invoke Thread.interrupted, which returns true if an interrupt has been received. For example:

for (int i = 0; i < Inputs.length; i++) {
  heavyCrunch(inputs[i]);
  if (Thread.interrupted()) {
    // We've been interrupted: no more crunching.
    return;
  }
}

In this simple example, the code simply tests for the interrupt and exits the thread if one has been received. In more complex applications, it might make more sense to throw an InterruptedException:

if (Thread.interrupted()) {
  throw new InterruptedException();
}

This allows interrupt handling code to be centralized in a catch clause.

The Interrupt Status Flag

The interrupt mechanism is implemented using an internal flag known as the interrupt status. Invoking THRead.interrupt sets this flag. When a thread checks for an interrupt by invoking the static method Thread.interrupted, interrupt status is cleared. The non-static THRead.isInterrupted, which is used by one thread to query the interrupt status of another, does not change the interrupt status flag.

By convention, any method that exits by throwing an InterruptedException clears interrupt status when it does so. However, it's always possible that interrupt status will immediately be set again, by another thread invoking interrupt.

Joins

The join method allows one thread to wait for the completion of another. If t is a THRead object whose thread is currently executing:

t.join();

causes the current thread to pause execution until t's thread terminates. Overloads of join allow the programmer to specify a waiting period. However, as with sleep, join is dependent on the OS for timing, so you should not assume that join will wait exactly as long as you specify.

Like sleep, join responds to an interrupt by exiting with an InterruptedException.

The SimpleThreads Example

The following example brings together some of the concepts of this section. SimpleThreads[7] consists of two threads. The first is the main thread that every Java application has. The main thread creates a new thread from the Runnable object, MessageLoop, and waits for it to finish. If the MessageLoop thread takes too long to finish, the main thread interrupts it.

[7] tutorial/essential/concurrency/example/SimpleThreads.java

The MessageLoop tHRead prints out a series of messages. If interrupted before it has printed all its messages, the MessageLoop tHRead prints a message and exits:

public class SimpleThreads {

  // Display a message, preceded by the name
  // of the current thread
  static void threadMessage(String message) {
    String threadName = Thread.currentThread().getName();
    System.out.format("%s: %s%n", threadName, message);
  }

  private static class MessageLoop implements Runnable {
    public void run() {
      String importantInfo[] = {
        "Mares eat oats",
        "Does eat oats",
        "Little lambs eat ivy",
        "A kid will eat ivy too"
      };
      try {
        for (int i = 0; i < importantInfo.length; i++) {
          // Pause for 4 seconds
          Thread.sleep(4000);
          // Print a message
          threadMessage(importantInfo[i]);
        }
      } catch (InterruptedException e) {
          threadMessage("I wasn't done!");
      }
    }
  }

  public static void main(String args[])
      throws InterruptedException {

    // Delay, in milliseconds before we interrupt MessageLoop
    // thread (default one hour).
    long patience = 1000 * 60 * 60;

    // If command line argument present,
    // gives patience in seconds.
    if (args.length > 0) {
      try {
          patience = Long.parseLong(args[0]) * 1000;
      } catch (NumberFormatException e) {
          System.err.println("Argument must be an integer.");
          System.exit(1);
      }
    }

    threadMessage("Starting MessageLoop thread");
    long startTime = System.currentTimeMillis();
    Thread t = new Thread(new MessageLoop());
    t.start();

    threadMessage("Waiting for MessageLoop thread to finish");
    // loop until MessageLoop thread exits
    while (t.isAlive()) {
      threadMessage("Still waiting...");
      // Wait maximum of 1 second for MessageLoop thread to
      // finish.
      t.join(1000);
      if (((System.currentTimeMillis() - startTime) > patience)
                                              && t.isAlive()) {
        threadMessage("Tired of waiting!");
        t.interrupt();
        // Shouldn't be long now -- wait indefinitely
        t.join();
      }

    }
    threadMessage("Finally!");
  }
}



 Python   SQL   Java   php   Perl 
 game development   web development   internet   *nix   graphics   hardware 
 telecommunications   C++ 
 Flash   Active Directory   Windows