1.124 Lecture 15 | 10/31/2000 |
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 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();
}
}
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;
}
}
}