Threads

Goal

Concepts

Language

Library

Lesson

The central processing unit (CPU) of your computer, even if it is several years old, probably has several cores that allow it to perform several tasks literally at the same time. Even on single-core machines, modern operating systems provide the ability to multitask, allowing several programs be open and running simultaneously. Each program is placed inside a process with its own allocation of memory and other resources.

If possible each core of the CPU is assigned to the processor so that it can truly run simultaneous or in parallel with the other processes. If your computer has multiple processors, even better—each processor will be assigned a separate process. Even if it is not possible to run the processes literally at the same instance, the operating system will switch between the processes very quickly to provide the illusion that they are operating at the same time. Either way, each process will complete its tasks concurrently with the other processes—independently but during the same space of time.

But multitasking is beneficial far beyond simply performing two tasks at once. It turns out that many programs spend much of their time waiting—on the user to enter something, or for something to be downloaded from the Internet. While a process is waiting, the operating system can suspend that process and let another one run, waking up the first process when data is ready. This is much more efficient than holding up the entire system just waiting for something to happen outside the computer.

Threads

All the programs you've written during this course have been one continuous sequence of logic. There have been multiple classes using many methods, this is true, but control was handed sequentially from class to class, from method to method. While one part of your program was waiting to write to the disk, your entire program had to wait until this operation was finished. So far you have not provided any way for another part of your program to do any work simultaneously.

This single path of execution within the program is called a thread of execution. A process will always have at least one thread, but it may have more. All the threads in a process share memory and resources; they could be considered lightweight processes.

The Java class that represents a thread is java.lang.Thread. You can get the object representing the current thread of execution by calling Thread.currentThread(). Each thread has several properties available, including Thread.getName(), Thread.getPriority(), and Thread.getState():

Getting information about the current thread of execution.
final Thread currentThread = Thread.currentThread();
getLogger().info("Current thread name: {}", currentThread.getName());  //prints "main"
getLogger().info("Current thread priority: {}", currentThread.getPriority());  //prints "5"
getLogger().info("Current thread state: {}", currentThread.getState());  //prints "RUNNABLE"

Thread State

At any time Thread instance is in one of several possible states, represented by the Thread.State enum. A newly created thread start out as NEW, and after it has been started it is RUNNABLE. When a thread has ended it is in the TERMINATED state. Before being terminated a thread may be BLOCKED or WAITING/TIMED_WAITING, as you will learn later.

Thread state diagram.
Thread state diagram.

Thread Priority

While the thread is in the RUNNABLE state, the thread scheduler may be executing on a CPU core. If there are more RUNNABLE threads than cores, the scheduler will divide up the time each thread gets to actually execute. Setting the thread priority using Thread.setPriority(int newPriority) can help the scheduler determine which thread should get more CPU time.

Thread Name

To make it easier to identify and distinguish threads, within a debugger for example, you may give a thread an interesting name using Thread.setName(String name).

Starting a New Thread

The main motive of creating a new Thread instance is to potentially allow some activity to run simultaneously with other activity. Creating and starting a new thread is as simple as calling Thread.start().

new Thread().start();

But such a thread will not do anything. Rather than calling its default constructor, call the constructor that takes a java.lang.Runnable interface. Runnable uses the strategy design pattern to specify in its Runnable.run() method the activity that should occur while running in a thread.

Executing code in a separate thread.
final Runnable grandSumCalculation = new Runnable() {
  @Override
  public void run() {
    long sum = 0;
    for(int n = 1; n<=1000; n++) {
      sum += n;
    }
    getLogger().info("Grand sum: {}", sum);
  }
}
final Thread calculationThread = new Thread(grandSumCalculation);
calculationThread.start();
//TODO do something else while the calculation is running in another thread

Immediately after calling Thread.start(), the method will return and your program will continue executing on the next line! What has happened behind the scenes is that a thread has been scheduled to execute the code within the Runnable—in fact it may already be running, even while the program that created that thread continues to execute!

Terminating a Thread

A thread will automatically terminate when its path of execution completes the Runnable.run() method (either by reaching the end of the method, encountering return, or throwing an exception). Once a thread has terminated, you cannot call Thread.start() to run it again.

Many times you want a thread to continue running until it is told to stop. The correct way to implement this is to have a flag or other condition to indicate that the thread should end. The thread will check this condition and leave the Runnable.run() method when the condition is recognized. For example, one could have a Runnable implementation BlinkerController that controls the blinker on a car. Once started, it would continue until it was told it should be turned using BlinkerController.turnOff().

A blinker that runs until a flag turns it off.
public class BlinkerController implements Runnable {

  /** Flag for turning off the blinker. */
  private volatile boolean turnOff = false;

  /** Tells the blinker to stop blinking. */
  public void turnOff() {
    turnOff = true;
  }

  /** Whether the blinker is lit or not. */
  private boolean blinkerLit = false;

  @Override
  public void run() {
    while(!turnOff) {
      blinkerLit = !blinkerLit;  //toggle blinker
      getLogger().info("Blinker {}", blinkerLit ? "on" : "off");
    }
  }
}

Some program can start the blinker by running an instance of BlinkerController in a Thread object. When it come time to turn off the blinker, the other thread will call BlinkerController.turnOff(). The thread controlling the blinker will recognize that the flag was set, and exit the run() method.

Starting a blinker controller in a separate thread.
public static void main(final String[] args) {
  final BlinkerController blinkerController = new BlinkerController();
  new Thread(blinkerController).start();  //tell the thread to start
  …
  //after some time, turn the blinker off
  blinkerController.turnOff();  //tell the BlinkerController to turn off
}

Sleeping Threads

The above blinker controller will work, but it will blink very quickly, immediately alternating between the blinker on and blinker off state. We expect blinkers to pause between each state—perhaps for half a second. The naive approach to adding this delay might be to check the system clock using System.currentTimetMilliseconds() and loop until 500 milliseconds had passed:

Incorrectly delaying using a loop to continually check the current time.
while(!turnOff) {
  blinkerLit = !blinkerLit;  //toggle blinker
  getLogger().info("Blinker {}", blinkerLit ? "on" : "off");
  //WRONG WAY TO DELAY
  final long delayTargetMilliseconds = System.currentTimeMillis() + 500;
  while(System.currentTimeMillis() < delayTargetMilliseconds) {
    //do nothing in particular while we're waiting
  }
}

The problem with this approach is that while the thread is waiting for 500 milliseconds to pass, it isn't really doing nothing; it is in fact looping and checking the current time, over and over. This takes up CPU resources which other threads could be using. If a thread really wants to do nothing for some amount of time, it can sleep by calling Thread.sleep(long milliseconds). This indicates that the operating system should suspend the execution of the current thread for approximately the amount of time requested before the thread is woke up to continue execution. (The exact amount of time may vary based on system load and precision of the system clock.)

Correctly delaying by putting a thread to sleep.
while(!turnOff) {
  blinkerLit = !blinkerLit;  //toggle blinker
  getLogger().info("Blinker {}", blinkerLit ? "on" : "off");
  try {
    Thread.sleep(500);
  } catch(final InterruptedException interruptedException) {
    //we woke up early for some reason, but we don't care
  }

Interrupting a Thread

If a thread is sleeping or waiting on some I/O to occur (such as reading from a traditional hard drive), another thread can interrupt it by calling the Thread.interrupt() method of the Thread instance. A thread must be programmed to handle interruption. Interrupting a thread sets its interrupt status flag.

So if a thread uses a method that can throw InterruptedException, the thread will usually handle the interrupt status automatically and need only deal with the generated InterruptedException.

Here is an updated version of the BlinkerController class that will interrupt the sleeping thread when the blinker is requested to be turned off, so that the blinker thread may immediately wake up and check its turnOff variable when turnOff() is called. As a bonus, the updated class also allows the amount of delay to be customized using the java.util.concurrent.TimeUnit enum to ease the representation of time values.
Interrupting a thread.
public class BlinkerController extends Thread {

  private final long delay;

  private final TimeUnit timeUnit;

  /** Flag for turning off the blinker. */
  private volatile boolean turnOff = false;

  /** Tells the blinker to stop blinking. */
  public void turnOff() {
    turnOff = true;
    interrupt();  //interrupt the thread if it is sleeping
  }

  public BlinkerController(final long delay, @Nonnull final TimeUnit timeUnit) {
   this.delay = delay;
   this.timeUnit = timeUnit;
  }

  /** Whether the blinker is lit or not. */
  private boolean blinkerLit = false;

  @Override
  public void run() {
    while(!turnOff) {
      blinkerLit = !blinkerLit;  //toggle blinker
      getLogger().info("Blinker {}", blinkerLit ? "on" : "off");
      try {
        timeUnit.sleep(delay);  //convenience method; delegates to Thread.sleep(…)
      } catch(final InterruptedException interruptedException) {
        //We woke up early for some reason, but we don't care;
        //what is important is that we check the turnOff flag.
      }
    }
  }
}

Now we would create and start the blinker controller thread like this:

Starting a blinker controller thread with specified delay.
public static void main(final String[] args) {
  final BlinkerController blinkerController = new BlinkerController(500, TimeUnit.MILLISECONDS);
  blinkerController.start();  //tell BlinkerController to start, because now we have made it a Thread
  …
  //after some time, turn the blinker off
  blinkerController.turnOff();  //tell the BlinkerController to turn off
}

Joining a Thread

Using the classes and techniques described in this lesson, you could wind up with two, three, or hundreds of threads running at the same time. Some of them could be controlling a blinker; others could be performing a calculation. You might want one thread to wait until another thread has completed before continuing. You can use the Thread.join() method to join the threads of execution together, so that the calling thread essentially sleeps until the thread on which join() is called completes.

Joining a thread to wait for it to finish.
final Thread calcThread = new Thread(new SomeCalculation());
calcThread.start();
//do something else while the other thread is running
…
//now we need to use the results of the other calculation
calcThread.join();  //wait for other calculation to finish

Daemon Threads

By default a Java program will not end until all threads that have been started by the program have finished. If you don't want a running thread to hold up the completion of a program, you can call Thread.setDaemon(boolean on) on a thread with the value of true. This will make the thread a daemon and will not prevent the program from ending, even if the thread is still running.

Review

Gotchas

In the Real World

Think About It

Self Evaluation

Task

Option Alias Description
list Lists all available publications.
load-snapshot Loads the snapshot list of publications into the current repository.
purchase Removes a single copy of the book identified by ISBN from stock.
subscribe Subscribes to a year's worth of issues of the periodical identified by ISSN.
--debug -d Includes debug information in the logs.
--help -h Prints out a help summary of available switches.
--isbn Identifies a book, for example for the purchase command.
--issn Identifies a periodical, for example for the subscribe command.
--locale -l Indicates the locale to use in the program, overriding the system default. The value is in language tag format.
--name -n Indicates a filter by name for the list command.
--type -t Indicates the type of publication to list, either book or periodical. If not present, all publications will be listed.

See Also

References