Threads I

What are "threads"?

In a single-threaded program, which is what you've been dealing with up til now, an executing program consists of a function call stack that grows and shrinks as functions are called and functions return. At the bottom of that function call stack is the call record for main(), and when that call returns, the program is over. In a multi-threaded program, there are multiple function call stacks that execute simultaneously. The "main thread" has the main() function call record on the bottom of the stack - other executing function call stacks (an executing function-call stack is called a thread) may have different function calls on the bottom-most function call record. The program ends when every thread has exited, i.e. when the bottom-most function-call record of each call stack has returned.

Ignoring for the moment the very important issues surrounding how the different threads (i.e. the different executing function call stacks) communicate with, and otherwise interact with one another, the fundamental problems that have to be addressed when designing a threading API are:

  1. How do you start a new thread? I.e. how do you call a function so that instead of the call resulting in a new function call record being pushed on top of the stack record of the caller in the caller's function call stack, a new stack is created and the function call becomes the bottom-most record on the new stack?
  2. How do you get data into the newly created function call stack when you start it?
  3. How do you get data out of the new function call stack once it's finished executing?

Java threads: the very basic basics

You might be surprised to find that Java's answer to this involves ... classes, inheritance and polymorphism. Are you surprised? I hope not! The Java API has a class called Thread that includes two important methods:

public void run();
public void start();
Creating a new thread (i.e. a new function call stack) works like this:
  1. an instance of Thread is created
  2. the start() method is called on the instance
  3. the run() method is called by start(), and that call becomes the bottom-most record on a new function call stack
So, if you have something specific you want a new thread to do, you derive a new class from Thread (extend Thread) and override the run() method to do whatever it is you want done.

Ex0.java
import java.util.*;

public class Ex0 {
  public static void main(String[] args) {
    Thread t = new Foo();

    t.start();
    int x = 5 * 5;
    System.out.println(x);
  }
}
Foo.java
public class Foo extends Thread {
  public void run() {
    int x = 7 * 7;
    System.out.println(x);
  }
}
Hopefully the following picture gives you some idea of how this works.

Some important points to take away are:

Getting data in and out: it's all in the Object

If you look at the above picture, what should strike you the Thread object - more literally the Foo object in this example - is pointed to by references from both threads (from both call stacks). That means that it can be a repository of data that we want to share between threads. We can add fields to Foo for data we want to communicate between the two threads. Below is an example of a fun little program that uses the Thread object to communicate data from the main thread to the new thread. I want you to pay attention to how both threads (both call stacks) make calls to the same methods: Ex1.printSlow().

Ex1.java
import java.util.*;

public class Ex1 {
  public static Random rand = new Random();

  public static void printSlow(String s, String t) {
    for (int i = 0; i < s.length(); i++) {
      try {
        Thread.sleep(rand.nextInt(1000));
      } catch (Exception e) {}
      System.out.println(t + s.charAt(i));
    }
  }

  public static void main(String[] args) {
    String s = "Mississippi";
    Thread t = new Foo(s, "   ");

    t.start();
    printSlow(s, " ");
  }
}
Foo.java
public class Foo extends Thread {
  private String msg, tab;

  public Foo(String s, String t) {
    this.msg = s;
    this.tab = t;
  }

  public void run() {
    Ex1.printSlow(this.msg, this.tab);
  }
}


Not pictured here is the Random object.