1.124J | Fall 2000 | Graduate

Foundations of Software Engineering

Lecture Notes

Multithreading

Topics

  1. Threads, Processes and Multitasking
  2. How to Create Threads
  3. The LifeCycle of a Thread
  4. Animations

1. Threads, Processes and Multitasking

Multitasking is the ability of a computer’s operating system to run several programs (or processes) concurrently on a single CPU. This is done by switching from one program to another fast enough to create the appearance that all programs are executing simultaneously. There are two types of multitasking:

Preemptive multitasking. In preemptive multitasking, the operating system decides how to allocate CPU time slices to each program. At the end of a time slice, the currently active program is forced to yield control to the operating system, whether it wants to or not. Examples of operating systems that support premptive multitasking are Unix®, Windows® 95/98, Windows® NT and the planned release of Mac® OS X.

Cooperative multitasking. In cooperative multitasking, each program controls how much CPU time it needs. This means that a program must cooperate in yielding control to other programs, or else it will hog the CPU. Examples of operating systems that support cooperative multitasking are Windows® 3.1 and Mac® OS 8.5.

Multithreading extends the concept of multitasking by allowing individual programs to perform several tasks concurrently. Each task is referred to as a thread and it represents a separate flow of control. Multithreading can be very useful in practical applications. For example, if a web page is taking too long to load in a web browser, the user should be able interrupt the loading of the page by clicking on the stop button. The user interface can be kept responsive to the user by using a separate thread for the network activity needed to load the page.

What then is the difference then between a process and a thread? The answer is that each process has its own set of variables, whereas threads share the same data and system resources. A multithreaded program must therefore be very careful about the way that threads access and modify data, or else unpredictable behavior may occur.

2. How to Create Threads

(Ref. Java® Tutorial)

We can create a new thread using the Thread class provided in the java.lang package. There are two ways to use the Thread class.

  • By creating a subclass of Thread.
  • By writing a class that implements the Runnable interface.

Subclassing the Thread class

In this approach, we create a subclass of the Thread class. The Thread class has a method named run(), which we can override in our subclass. Our implementation of the run() method must contain all code that is to be executed within the thread.

class MyClass extends Thread {
// …

public void run() {
// All code to be executed within the thread goes here.
}
}
 

We can create a new thread by instantiating our class, and we run it by calling the start() method that we inherited from class Thread.

MyClass a = new MyClass();
a.start();

This approach for creating a thread works fine from a technical standpoint. Conceptually, however, it does not make that much sense to say that MyClass “is a” Thread. All that we are really interested in doing is to provide a run() method that the Thread class can execute. The next approach is geared to do exactly this.

Implementing the Runnable Interface

In this approach, we write a class that implements the Runnable interface. The Runnable interface requires us to implement a single method named run(), within which we place all code that is to be executed within the thread.

class MyClass implements Runnable {
// …

public void run() {
// All code to be executed within the thread goes here.
}
}
 

We can create a new thread by creating a Thread object from an object of type MyClass. We run the thread by calling the Thread object’s start() method.

MyClass a = new MyClass;
Thread t = new Thread(a);
t.start();

3. The LifeCycle of a Thread

(Ref. Java® Tutorial)

A thread can be in one of four states during its lifetime:

  • new - A new thread is one that has been created (using the new operator), but has not yet been started.

  • runnable - A thread becomes runnable once its start() method has been invoked. This means that the code in the run() method can execute whenever the thread receives CPU time from the operating system.

  • blocked - A thread can become blocked if one of the following events occurs:

    • The thread’s sleep() method is invoked. In this case, the thread remains blocked until the specified number of milliseconds elapses.
    • The thread calls the wait() method of an object. In this case, the thread remains blocked until either the object’s notify() method or its notifyAll() method is called from another thread. The calls to wait(), notify() and notifyAll() are typically found within synchronized methods of the object.
    • The thread has blocked on an input/output operation. In this case, the thread remains blocked until the i/o operation has completed.
  • dead - A thread typically dies when the run() method has finished executing.

Note: The following methods in the java.lang.Thread class should no longer be used, since they can lead to unpredicable behavior: stop(), suspend() and resume().

The following example illustrates various thread states. The main thread in our program creates a new thread, Thread-0. It then starts Thread-0, thereby making Thread-0 runnable so that it prints out an integer every 500 milliseconds. We call the sleep() method to enforce the 500 millisecond delay between printing two consecutive integers. In the meantime, the main thread proceeds to print out an integer every second only. The output from the program shows that the two threads are running in parallel. When the main thread finishes its for loop, it stops Thread-0.

We maintain a variable, myThread, which initially references Thread-0. This variable is polled by the run() method to make sure that it is still referencing Thread-0. All we have to do to stop the thread is to set myThread to null. This will cause the run() method to terminate normally.

class MyClass implements Runnable {
int i;
Thread myThread;

public MyClass() {
i = 0;
}

// This will terminate the run() method.
public void stop() {
myThread = null;
}

// The run() method simply prints out a sequence of integers, one every half second.
public void run() {
// Get a handle on the thread that we are running in.
myThread = Thread.currentThread();

// Keep going as long as myThread is the same as the current thread.
while (Thread.currentThread() == myThread) {
System.out.println(Thread.currentThread().getName() + “: " + i);
i++;

try {
Thread.sleep(500); // Tell the thread to sleep for half a second.
}
catch (InterruptedException e) {}
}
}
}
 

class Threadtest {
public static void main(String[] args) {
MyClass a = new MyClass();
Thread t = new Thread(a);

// Start another thread.  This thread will run in parallel to the main thread.
System.out.println(Thread.currentThread().getName() + “: Starting a separate thread”);
t.start();

// The main thread proceeds to print out a sequence of integers of its own, one every second.
for (int i = 0; i < 6; i++) {
System.out.println(Thread.currentThread().getName() + “: " + i);
// Tell the main thread to sleep for a second.
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {}
}

// Stop the parallel thread.  We do this by setting myThread to null in our runnable object.
System.out.println(Thread.currentThread().getName() + “: Stopping the thread”);
a.stop();
}
}

4. Animations

Here is an example of a simple animation. We have used a separate thread to control the motion of a ball on the screen.

anim.html

<HTML>
<BODY>
<APPLET CODE=“Animation.class” WIDTH=300 HEIGHT=400>
</APPLET>
</BODY>
 

Animation.java

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class Animation extends JApplet implements Runnable, ActionListener {
int miFrameNumber = -1;
int miTimeStep;
Thread mAnimationThread;
boolean mbIsPaused = false;
Button mButton;
Point mCenter;
int miRadius;
int miDX, miDY;

public void init() {
// Make the animation run at 20 frames per second.  We do this by
// setting the timestep to 50ms.
miTimeStep = 50;

// Initialize the parameters of the circle.
mCenter = new Point(getSize().width/2, getSize().height/2);
miRadius = 15;
miDX = 4;  // X offset per timestep.
miDY = 3;  // Y offset per timestep.

// Create a button to start and stop the animation.
mButton = new Button(“Stop”);
getContentPane().add(mButton, “North”);
mButton.addActionListener(this);

// Create a JPanel subclass and add it to the JApplet.  All drawing
// will be done here, do we must write the paintComponent() method.
// Note that the anonymous class has access to the private data of
// class Animation, because it is defined locally.
getContentPane().add(new JPanel() {
public void paintComponent(Graphics g) {
// Paint the background.
super.paintComponent(g);

// Display the frame number.
g.drawString(“Frame " + miFrameNumber, getSize().width/2 - 40,
getSize().height - 15);

// Draw the circle.
g.setColor(Color.red);
g.fillOval(mCenter.x-miRadius, mCenter.y-miRadius, 2*miRadius,
2*miRadius);
}
}, “Center”);
}

public void start() {
if (mbIsPaused) {
// Don’t do anything.  The animation has been paused.
} else {
// Start animating.
if (mAnimationThread == null) {
mAnimationThread = new Thread(this);
}
mAnimationThread.start();
}
}

public void stop() {
// Stop the animating thread by setting the mAnimationThread variable
// to null.  This will cause the thread to break out of the while loop,
// so that the run() method terminates naturally.
mAnimationThread = null;
}

public void actionPerformed(ActionEvent e) {
if (mbIsPaused) {
mbIsPaused = false;
mButton.setLabel(“Stop”);
start();
} else {
mbIsPaused = true;
mButton.setLabel(“Start”);
stop();
}
}

public void run() {
// Just to be nice, lower this thread’s priority so it can’t
// interfere with other processing going on.
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);

// Remember the starting time.
long startTime = System.currentTimeMillis();

// Remember which thread we are.
Thread currentThread = Thread.currentThread();

// This is the animation loop.
while (currentThread == mAnimationThread) {
// Advance the animation frame.
miFrameNumber++;

// Update the position of the circle.
move();

// Draw the next frame.
repaint();

// Delay depending on how far we are behind.
try {
startTime += miTimeStep;
Thread.sleep(Math.max(0,
startTime-System.currentTimeMillis()));
}
catch (InterruptedException e) {
break;
}
}
}

// Update the position of the circle.
void move() {
mCenter.x += miDX;
if (mCenter.x - miRadius < 0 ||
mCenter.x + miRadius > getSize().width) {
miDX = -miDX;
mCenter.x += 2*miDX;
}

mCenter.y += miDY;
if (mCenter.y - miRadius < 0 ||
mCenter.y + miRadius > getSize().height) {
miDY = -miDY;
mCenter.y += 2*miDY;
}
}
}

Course Info

Learning Resource Types
Exams with Solutions
Presentation Assignments
Programming Assignments with Examples
Written Assignments