| 1.124 Lecture 14 | 11/3/998 |
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.
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.
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();
A thread can be in one of four states during its lifetime:
The following example illustrates various thread states. The main thread in our program creates a separate thread, Thread-2. It then starts Thread-2, thereby making Thread-2 runnable and able to print out an integer every 500 milliseconds. In the meantime, the main thread goes to sleep for 5 seconds. After the main thread wakes up, it suspends Thread-2, thereby causing Thread-2 to become blocked. The main thread then goes to sleep for another 5 seconds. After the main thread wakes up, it resumes Thread-2, thereby causing Thread-2 to become runnable again. The main thread then goes to sleep yet again. When the main thread wake up this time, it stops Thread-2, thereby causing Thread-2 to die.
class MyClass implements Runnable {
int i;
public MyClass() {
i = 0;
}
// The run() method simply prints out a sequence
of integers, one every half second.
public void run() {
while (true) {
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 the thread.
System.out.println(Thread.currentThread().getName()
+ ": Starting a separate thread");
t.start();
// Tell the main program
to sleep for 5 seconds.
try {
Thread.sleep(5000);
}
catch (InterruptedException
e) {}
// Suspend execution of
the thread.
System.out.println(Thread.currentThread().getName()
+ ": Suspending the thread");
t.suspend();
// Tell the main program
to sleep for 5 seconds.
try {
Thread.sleep(5000);
}
catch (InterruptedException
e) {}
// Resume execution of
the thread.
System.out.println(Thread.currentThread().getName()
+ ": Resuming the thread");
t.resume();
// Tell the main program
to sleep for 5 seconds.
try {
Thread.sleep(5000);
}
catch (InterruptedException
e) {}
// Stop the thread.
System.out.println(Thread.currentThread().getName()
+ ": Stopping the thread");
t.stop();
}
}
Animation.java
import java.awt.*;
import java.awt.event.*;
import java.applet.Applet;
public class Animation extends Applet 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() {
miTimeStep = 50; // Time
step in milliseconds.
// Create a button to
start and stop the animation.
mButton = new Button("Stop");
add(mButton);
mButton.addActionListener(this);
// 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.
}
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.
if (mAnimationThread
!= null) {
mAnimationThread.stop();
}
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;
}
}
}
// Draw a frame.
public void paint(Graphics 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);
}
// 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;
}
}
}