Threads: Introduction
AAHC

Click on A to make all fonts on the page smaller.

Click on A to make all fonts on the page larger.

Click on HC to toggle high contrast mode. When you move your mouse over some bold words in high contrast mode, related words are automatically highlighted. Text is shown in black and white.


Multi-Tasking

In the next few lessons, we'll explore the ways Java allows us to coordinate multiple tasks. Programmers need be able to determine which operations are executed and in what order. The Java programming language allows us to write various individual programs that, while running seperately and concurrently, appear as a single, unified and seamless operation to the user. This is accomplished by providing mechanisms for synchronizing the concurrent activity of threads.

Threads

A thread is a single sequential flow of control within a program. The API says, "A thread is a thread of execution in a program. The Java Virtual Machine allows an application to have multiple threads of execution running concurrently."

Before we discuss the theory of Threads, we'll look at the tools Java provides for us to weave them into our code. We will look at Threads themselves more explicitly in the next lesson.

Go to the java.lang package and scroll down to the Thread class and read its introduction. A thread has these qualities:

We can take advantage of the capabilities of Threads in Java by:

  1. subclassing the Thread class.
  2. implementing the Runnable interface.

We'll demonstrate both of these techniques in this lesson.


Subclassing the Thread Class

Create a new java4_Lesson8 project. If you're offered the option to Open Associated Perspective, click No. In this project, create a new SimpleThread class as shown:

Type SimpleThread as shown in blue:

CODE TO TYPE: SimpleThread
package demo;

class SimpleThread extends Thread {

    public SimpleThread(String str) {
        super(str);
    }

    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(i + " " + getName());
            try {
                sleep((int)(Math.random() * 1000));
            }                                       // end try
            catch (InterruptedException e) {
            }                                       // end catch
        }                                           // end for loop
        System.out.println("DONE! " + getName());
    }                                                        // end run method
}

There are two snippets of code here that we haven't seen before:

  1. super(str)
  2. the try/catch clause surrounding the sleep() method

Let's go to the API to find out more about this code. Go to java.lang.Thread and check out the inheritance tree. Our SimpleThread class inherits from Thread. What would Thread Constructor inherit if a String was passed to it via super(str)?

Go to the java.lang.Thread Method Summary and look at the methods there. Both methods present throw InterruptedExceptions. We need to handle them using try/catch clauses.

We've seen the static method call Math.random() and casting before, so this might look familiar to you already. If not, go to the java.lang.Math class and look at the random() method.

Save and run SimpleThread.

You see a menu choice Open Run Dialog, and then a window:

Go ahead and choose Run. Hmm. Something's wrong. There are a number of questions to consider:

Let's add a main() method to test. Edit SimpleThread as shown in blue below:

CODE TO EDIT: SimpleThread
package demo;

class SimpleThread extends Thread {

    public SimpleThread(String str) {
       super(str);
    }

    public void run() {
       for (int i = 0; i < 10; i++) {
           System.out.println(i + " " + getName());
           try {
               sleep((int)(Math.random() * 1000));
           } 
           catch (InterruptedException e) {
           }
       }				       
       System.out.println("DONE! " + getName());
    }

    public static void main (String [] args){
        SimpleThread st = new SimpleThread("myGuy");
    }
}

Save and run it.

Nothing happened. That's because you have direct control of your thread, so you must start it explicitly. Java Threads need to be instantiated like any other class. However, you don't call the run() method explicitly; you begin by invoking the start() method. Edit SimpleThread again. Add the code in blue as shown:

CODE TO EDIT: SimpleThread
package demo;

class SimpleThread extends Thread {

    public SimpleThread(String str) {
       super(str);
    }

    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(i + " " + getName());
            try {
                sleep((int)(Math.random() * 1000));
            } 
            catch (InterruptedException e) {
            }
        }				       
        System.out.println("DONE! " + getName());
    }

    public static void main (String [] args){
        SimpleThread st = new SimpleThread("myGuy");
        st.start(); 
    }
}

Save and run it.

NoteStarting Threads is different from starting other classes and methods. Threads must have a run() method in order to operate, but you start the run() method by invoking the instance of the Thread with start().

Go to the java.lang.Thread class and look at its start() method. Here (and in general) we inherit the method start() from our thread SimpleThread's parent Thread.

In the instance of the Thread that we're running here, we instruct it to sleep() for a few milliseconds. This stops it from running for at least the specified time and then allows it to continue.

Let's see what happens when we comment out the try/catch block with the sleep call:

CODE TO EDIT: SimpleThread
package demo;

class SimpleThread extends Thread {

    public SimpleThread(String str) {
        super(str);
    }

    public void run() {
        for (int i = 0; i < 10; i++) {
           System.out.println(i + " " + getName());
           //try {
           //    sleep((int)(Math.random() * 1000));
           //} 
           //catch (InterruptedException e) {
           //}
        }				       
        System.out.println("DONE! " + getName());
    }

     public static void main (String [] args){
         SimpleThread st = new SimpleThread("myGuy");
         st.start(); 
     }
}

Save and run it. Take the comment slashes (//) out, then save and run it again. Putting threads to sleep() allows more time for other actions (from other threads) to take place.

Manipulating Threads

Let's experiment some more. Create a new AnotherThread class in the java4_Lesson8 project as shown:

Type AnotherThread as shown in blue:

CODE TO TYPE: AnotherThread
package demo;

// Threads are in java.lang.Thread so no import is needed

public class AnotherThread {

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

class T extends Thread {

    public void run() {
        while(true) {                         // forever, 
            System.out.println("b: ");        // prompt the user with a b:, waiting for them to do something
            try {
                sleep(300);  			      // sleep is in milliseconds
            }
            catch (InterruptedException e){}
        }  
    }  
}

We have defined the class AnotherThread, which has a nested class named T, which extends Thread. The variable t in the main() method of class AnotherClass should contain a valid thread of execution for an instance of the subclass of Thread that we named T. We control this thread in the run() method.

Once inside the run() method, we're able execute statements just like in any program. In these examples, we are pausing for a specified period of time. In our first example, the period of time was random; in the above class, it's 300 milliseconds. In our code it's written like this: sleep(300).

The sleep() method tells a thread to pause for at least the specified number of milliseconds. The sleep() method does not take up system resources while the thread sleeps. Other threads can continue to work.

Save and run it.

Normally, threads stop when their run() method is complete. In this thread, however, we have an infinite loop in the run() method.

[Ctrl+c] will stop most execution processes. However, in this case (that is, within Eclipse within a thread within our control), you click the Terminate box to stop execution:

Here's another example that uses threads and passes parameters. In the java4_Lesson8 project, add a new TestThread class as shown:

Type TestThread as shown in blue below:

CODE TO TYPE: TestThread
package demo;                                    // Define our simple threads.  They will pause for a short time
                                                 // and then print out their names
class TestThread extends Thread {
    private String whoAmI;
    private int delay;

    public TestThread(String s, int d) {  // Our constructor to receive the name (whoAmI) and time to sleep (delay)
        whoAmI = s;
        delay = d;
    }
                                          // run--the thread method similar to main()--when run is finished, the thread dies.
    public void run() {                   // run is called from the start() method of Thread
        try {
            sleep(delay);
        } 
        catch (InterruptedException e) {
        }
        System.out.println(whoAmI + " has delay time of " + delay);
    }
}

The run() method serves as the main() routine for threads. Just like main(), when run() finishes, so does the thread.

Applications use the main() method to retrieve their arguments from the args parameter (which is typically set in the command line). A newly created thread must receive its arguments programmatically from the originating thread. That way parameters can be passed in through the constructor, static variables, or any other technique designed by the developer. In the TestThread example, we pass the parameters through the constructor. We need to create a class to instantiate a few instances of this TestThread.

In the java4_Lesson8 project, add a new class named MultiTest as shown:

Type MultiTest as shown in blue below:

CODE TO TYPE: MultiTest
        
//  A simple multithread test program
package demo;
                                                                  
public class MultiTest {

    public static void main(String args[]) {
        TestThread t1, t2, t3;
        // Instantiate/create our test threads
        t1 = new TestThread("Thread1",(int)(Math.random()*1000));
        t2 = new TestThread("Thread2",(int)(Math.random()*2000));
        t3 = new TestThread("Thread3",(int)(Math.random()*3000));
        
        // Start each of the threads
        t1.start();
        t2.start();
        t3.start();  
        // At this point we have started 3 threads!
    }	
}

Save both TestThread and MultiTest.

Run MultiTest using its main() method.

Now, try changing the maximum number of random times as shown in blue:

CODE TO EDIT: MultiTest
        
//  A simple multithread test program
package demo;
       
public class MultiTest {
          
    public static void main(String args[]) {
        TestThread t1, t2, t3;
        // Instantiate/create our test threads
        t1 = new TestThread("Thread1",(int)(Math.random()*3000));
        t2 = new TestThread("Thread2",(int)(Math.random()*2000));
        t3 = new TestThread("Thread3",(int)(Math.random()*1000));
          
        // Start each of the threads
        t1.start();
        t2.start();
        t3.start();  
        // At this point we have started 3 threads!
    }   
}

Remember that these are random, so the order in which they appear does not necessarily indicate increased or decreased delays.

Save and run MultiTest again.


Threads in Applets

Applets can have Threads too. With a few adaptations, our application above can be turned into an Applet.

In java4_Lesson8, create a new MultiTestApplet class as shown:

Type MultiTest as shown:

CODE TO TYPE: MultiTestApplet
package demo;

import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
                                                                
public class MultiTestApplet extends Applet implements ActionListener {

    TestThread t1, t2, t3;

    public void init() {
        Button runUs = new Button("Run Threads");                  // create a Button 
        add(runUs);                                                // add it to the Applet
        runUs.addActionListener(this);                             // add a Listener to the Button

        t1 = new TestThread("Thread1",(int)(Math.random()*1000));  // instantiate our 3 TestThreads
        t2 = new TestThread("Thread2",(int)(Math.random()*2000));
        t3 = new TestThread("Thread3",(int)(Math.random()*3000));
    }

    public void actionPerformed(ActionEvent e){
        t1.start();                                                // clicking the Button will allow us to start the threads
        t2.start();
        t3.start(); 
    }
}

Save and run it. Click on the button. There's output in the Console because we used System.out.println. For now, close this Applet. We'll come back to it later when we look at Thread States.

Implementing the Runnable Interface

Sometimes we want a class to use a Thread, but we do not want that class to be a Thread. Java allows only single inheritance, so if a class inherits from Applet it cannot inherit from Thread at the same time. We get around this issue using Interfaces. You can also create a thread by declaring a class that implements the Runnable interface.

in the java.lang package, go to the Interface Summary and choose Runnable. Scroll down its description to see the methods that this interface specifies. You'll actually only see one method in the Method Summary: run().

If a class implements Runnable, then the class must implement the run() method. An instance of the class can then be allocated, passed as an argument when creating a Thread, and started. We'll go to the API to see what this means, and demonstrate it.

Go to the class java.lang.Thread and read through its constructors. Many of them have a parameter of type Runnable:

Let's try an example. In java4_Lesson8, create a new class as shown:

Type ThreadedApplet as shown in blue:

CODE TO TYPE: ThreadedApplet
package demo;

import java.applet.Applet;
import java.awt.Graphics;

public class ThreadedApplet extends Applet implements Runnable {

    Thread appletThread;      // the thread we make will be an instance of the class Thread
    String messages[] = {"Hello Thread World!" , "I'm doing fine." , "Goodbye for now!"};
    int i = 0;

    public void paint(Graphics g) {
	    g.drawString(messages[i], 15, 50);
    }

    public void run() {
        while (true){
	        i = (i+1)  % messages.length;
	        repaint();
	        try {
		        appletThread.sleep(5000);
	        } catch (InterruptedException e){}
	    }
    }

    public void start() {
        appletThread = new Thread(this);
        appletThread.start();
    }   
}

This program is an Applet with a run() method that cycles to print different messages. When the Applet starts, it instantiates the Thread and then starts the Thread instance. In this example, we passed the Thread Constructor an instance of an object that implements Runnable (that is, the Applet itself--this). The Thread then comes back to the Applet to get the run() method that it implemented. This is especially convenient, because then the ThreadedApplet's paint(Graphics g) method and the implemented run() method can share the messages[ ] instance variable.

Save and run it. Sit back and watch for a while.

Here the ThreadedApplet class has a start() method for the Applet. In this method, the thread is instantiated and its start() method (appletThread.start();) is invoked. Of course, we'll want a stop for our thread too. For now, you can stop the thread and applet by quitting the applet.

One More Time

To create a thread, you must subclass Thread and define this subclass's own run() method or you must pass a Runnable object--which means the object must implement a run() method--to the Thread Constructor.

The Runnable interface specifies the run() method that is required. Any class that implements this interface can provide the body of a thread. By implementing the Runnable interface, you declare your intention to run a separate thread.

Whichever way you look at threads, the operative word is run(). More details on Java Threads are on the way!