|
JavaFAQ Home » Java Lessons by Jon Huhtala

Multithreading, the Thread class, and the Runnable interface
Overview
In every day life, you frequently
perform two or more tasks simultaneously. For example, you could be talking on
the telephone while having a cup of coffee and watching television. Although you
might think you are performing all three tasks at the same time, you really
aren't. By quickly shifting your attention from one activity to another, you are
able to complete all three tasks without actually performing any two at
precisely the same time. In addition, the tasks may have different priorities.
One may be given more attention (time) than another and these priorities may
change.
In a similar manner, a Java
program can perform two or more processes simultaneously. When it does, it is
called multithreading.
Multithreading
-
Is a fundamental feature of the
Java language specification. The creators of Java understood the importance of
multithreading and provided easy to use features for application developers.
Two of these features, the Thread class and the Runnable interface, will be covered shortly.
-
Is not the same as
multiprocessing. The latter involves the use of two or more processors (CPUs).
Multithreading involves the sharing of a single processor and the interleaving
of CPU time as shown by the following diagram:
| |
CPU time |
| Thread 1 |
xxxxxxxxxxx
xxxxx |
| Thread 2 |
xxxxxxxxxxxxxxxx
xxxxxxxx
xxxxxx |
| Thread 3 |
xxxxxxx
xxxxxxxxxxx |
Because there is only one
processor, only one instruction can be executed at a time. In a Java program,
the decision as to which thread is currently executing is determined by the
JVM.
-
Is used to execute even the
simplest Java program. Even if a program doesn't create its own threads, the
Java Virtual Machine creates multiple threads to support the program. One
thread performs the processing of the main() method. Other threads manage and monitor system
resources. The garbage collector, for example, is always running as a
low-priority thread.
The life of a thread
In Java, all threads are objects
that are constructed, pass between several "states", and die in keeping with the
following diagram:
|
|
Runnable |
|
Blocked |
|
New |
---> |
|
<---> |
|
sleeping
waiting
suspended
| |
|
|
|
|
|
|
|
|
Dead |
|
-
When initially constructed, the
thread is New
-
When all resources the thread
requires are available, it enters the Ready state. The thread is ready
to use the CPU.
-
When selected by the JVM for
processing, the thread enters the Running state. This is the state that
all threads aspire to but only one is ever running at an instant in time.
-
When the thread requests to
sleep for an interval of time, must wait for a resource (such as completion of
I/O), or is suspended by the JVM, it becomes Blocked. It gives up the
CPU and will return to the Ready state when the block is removed.
-
When the thread completes its
processing or is killed by the JVM, it enters the Dead state. The
thread object will still exist, but will no longer be allowed to use the CPU.
The Thread class
-
Encapsulates the processing of a thread
-
Can be extended by a class that
must process as a thread. All that is required is to override the inherited
run() method to define
the thread's processing. For example,
public class GetMad extends Thread { public
void run() { for (int i = 1; i <= 10; i++)
{
System.out.println(i); }
System.out.println("DONE"); } }
defines a class to count from 1 to 10 as a
thread. For an application to create and launch the thread, all that must be
coded are the statements:
GetMad t = new GetMad(); t.start();
The inherited start() method of the Thread class automatically
calls the run() method to
trigger the thread's processing. A program should never call the run() method directly.
Once the thread has been
launched, the application can continue its own processing thread with the JVM
scheduling the use of the CPU by the two threads. Here is a complete example
using a static inner
class:
public class App
{
public static class GetMad extends Thread
{ public void run()
{ for (int i = 1; i <= 10; i++)
{
System.out.println(i);
} } }
public static void
main(String[] args) {
GetMad t = new
GetMad(); t.start(); for (int i
= 1; i <= 100; i++) {
System.out.println("Working"); }
} }
When this is run, note how the
output of the two threads is interleaved.
- Has several constructors. The most frequently used is demonstrated by
Thread aThread = new
Thread(threadObject);
where threadObject is the reference of
an object that either extends the Thread class or implements the Runnable interface.
Using this technique, an alternative way to have constructed the thread of
the previous sample would be
Thread t = new Thread(new
GetMad());
- Has a number of methods, some of which are static. The most frequently used methods are the
following:
|
Method |
Usage |
|
destroy() |
A method that may be overridden to define
what is to be done when the thread is to be destroyed |
|
getName() |
Returns a String representing the thread's
name |
|
getPriority() |
Returns an int from 1 to 10 that indicates the
thread's priority. The highest priority is 10. |
|
interrupt() |
Interrupts the thread |
|
isAlive() |
Returns a boolean indicating if the thread is
currently alive |
|
isInterrupted() |
Returns a boolean indicating if the thread is
currently interrupted |
|
run() |
A method that may be overridden to define
thread processing. It should never be called directly. |
|
setName() |
Sets the name of the thread to the
specified String |
|
setPriority() |
Sets the priority of the thread to the
specified int
value (from 1 to 10) |
|
sleep() |
Causes the currently executing thread to
sleep for a specified number of milliseconds. This is a static (class)
method. |
|
start() |
Causes this thread to begin execution by
automatically calling its run() method |
|
yield() |
Causes the currently executing thread to
give up the CPU so that other threads may execute. This is a static (class)
method. |
Note that the stop(), suspend(), and resume() methods of the Thread class are deprecated and should be avoided. They are
unsafe and may result in errors.
Consult the Java API
documentation for more details.
-
Provides a scheme for prioritizing a thread.
The priority of a thread is an int value in the range of 1 to 10 (where 1 is the lowest
priority and 10 is the highest priority). Unless specifically set, the
priority of a thread will be the same as the thread that launched it. For an
application thread, the default priority is 5.
-
Provides a general mechanism for setting a timer without creating a thread
object. The sleep()
method can be used to delay for a specified number of milliseconds as shown by
the following program:
public class App
{ public static void main(String[] args) {
System.out.println("Going to sleep for 3 seconds...");
try {
Thread.sleep(3000); } catch
(InterruptedException err) { }
System.out.println("Now I'm awake!"); } }
Note that sleep() is a
static method (requires
no object) and may throw an InterruptedException. A method that calls Thread.sleep() must either use
try and catch blocks (as shown in this
example) or claim the possibility of throwing the exception.
Thread.yield();
Note that yield() is a
static method (requires
no object) and throws no exceptions. It is a good practice for long-running
threads to periodically yield and allow other threads the opportunity to use
the CPU.
public class App
{
// This inner class encapsulates the processing of an Alarm
thread // that will display a message at a specified time
interval.
public static class Alarm extends Thread
{
// Instance variables.
private int delayInSeconds; private int
count; private boolean
isAlive;
// This method constructs an Alarm
thread. It receives the time // interval in seconds,
sets the count to zero, and indicates that // the
thread is alive.
public Alarm(int seconds)
{ delayInSeconds =
seconds; count =
0; isAlive = true;
}
// This method defines thread processing. The
thread will loop // while alive to display a message
at the time interval.
public void run()
{ while (isAlive)
{ try
{
Thread.sleep(delayInSeconds *
1000);
} catch (InterruptedException
err) {
} count +=
delayInSeconds;
System.out.println(getName() + ": " +
count); }
}
// This method can be called to destroy the
thread.
public void destroy()
{ isAlive = false;
} }
// This is the starting point for application
processing.
public static void main(String[] args)
{
// Instantiate three Alarm threads with
intervals of 1, 5, and 10 // seconds and start their
processing.
Alarm t1 = new
Alarm(1); Alarm t2 = new
Alarm(5); Alarm t3 = new
Alarm(10); t1.start();
t2.start(); t3.start();
//
Sleep for 20 seconds.
try
{
Thread.sleep(20000); } catch
(InterruptedException err) {
}
// Destroy all threads and release their
resources.
t1.destroy(); t1
= null; t2.destroy(); t2 =
null; t3.destroy(); t3 =
null; } }
Notes:
-
The value of the boolean variable isAlive is used to control thread processing in
the run() method.
-
The destroy() method overrides the default destroy() method inherited
from the Thread class
to set the value of isAlive to false (and stop thread processing).
The Runnable interface
public void
run()
The following is a sample applet that contains
an inner class for a blinking button. By extending the Button class, BlinkButton inherits normal
button features. By implementing the Runnable interface and defining a run() method, it adds thread processing to make
the button blink at a specified interval set by its constructor.
import java.awt.*; import java.awt.event.*; import
java.applet.*;
public class App extends Applet implements
ActionListener {
// This inner class defines a blinking
button. The Runnable // interface is implemented and the run()
method defined in // order for an object to be run as a
thread.
public class BlinkButton extends Button implements
Runnable {
// For holding the blink interval in
milliseconds.
int
interval;
// This method constructs a blinking
button object. It receives // the button's label
(which is passed through to the superclass //
constructor) and the blink speed in milliseconds (which
is // saved). It then creates and starts a thread for
this object.
public BlinkButton(String label, int
blinkSpeed) {
super(label); interval =
blinkSpeed; new
Thread(this).start(); }
//
This method is required by the Runnable interface and
defines // thread
processing.
public void run()
{
// Endless loop to sleep for the
specified interval then awake // to swap
the button's foreground and background
colors.
while (true)
{ try
{
Thread.sleep(interval);
} catch(InterruptedException
err) {
} Color oldForeground =
getForeground();
setForeground(getBackground());
setBackground(oldForeground);
} } }
// Object references for
this applet.
BlinkButton b; TextField
message;
// This method begins applet
processing.
public void init() {
//
Resize the applet and set its background color.
resize(300, 100);
setBackground(Color.lightGray);
// Create the
blink button and add it to the applet.
b = new
BlinkButton("Show message", 500);
b.setBackground(Color.black);
b.setForeground(Color.white);
b.addActionListener(this);
add(b);
// Create the message text field and add
it to the applet.
message = new TextField("",
15); message.setFont(new Font("Serif", Font.ITALIC,
24));
message.setForeground(Color.red);
message.setEditable(false); add(message);
}
// This method is automatically called to handle the
user // clicking the blink button.
public void
actionPerformed(ActionEvent e) {
// If the message
is not currently displayed, display it. // Otherwise,
clear the message.
if (message.getText().length()
== 0) { message.setText("Hello
world!"); b.setLabel("Clear
message"); } else
{
message.setText(""); b.setLabel("Show
message"); }
} }
Looking ahead
Multithreading is a powerful tool, but can be dangerous when two or more
threads share the same resource. If not properly managed, one thread can easily
overlay or undo the work of another thread. This will be addressed in the next
lesson.
Lab exercise for Ferris
students
E-mail your answers to this
assignment no later than
the due date listed in the class schedule.
Review questions
-
What will be displayed when
an attempt is made to compile and execute the following program? The line
numbers are for reference purposes only.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public
class App { public static class Inner extends Thread
{ public void run(int x)
{ for (int i = 0; i < 3; i++)
{
System.out.println("Thread
Running"); }
} } public static void main(String[] args)
{ Inner x = new Inner();
Thread t = new Thread(x); t.start();
} } |
-
a compile error will occur
at line 2
-
a compile error will occur
at line 3
-
a compile error will occur
at line 12
-
the program will compile
and execute to display the message "Thread
Running" 3 times
-
the program will compile
and execute but nothing will be displayed
-
What will be displayed when
an attempt is made to compile and execute the following program? The line
numbers are for reference purposes only.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public
class App { public static class Inner extends Thread
{ String myName; public
Inner(String name) { myName =
name; } public void run()
{ while (true)
{
System.out.println(myName);
} } } public static void
main(String[] args) { Thread t1 = new Thread(new
Inner("Bob"));
t1.setPriority(4);
t1.start(); Thread t2 = new Thread(new
Inner("Sally")); t2.start();
} } |
-
a compile error will occur
at line 14
-
a compile error will occur
at line 15
-
the program will compile
and execute to display "Bob" more
than "Sally"
-
the program will compile
and execute to display "Sally" more
than "Bob"
-
the program will compile
and execute but nothing will be displayed
-
What will be displayed when
an attempt is made to compile and execute the following program? The line
numbers are for reference purposes only.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public
class App { public static class Inner extends Thread
{ public void run()
{ while (true)
{
System.out.println("Sub
Thread");
Thread.yield();
} } } public static void
main(String[] args) { new Thread(new
Inner()).start(); while(true)
{ System.out.println("Main
Thread"); }
} } |
-
a compile error will occur
at line 6
-
a compile error will occur
at line 11
-
the program will compile
and execute to display "SubThread"
more than "MainThread"
-
the program will compile
and execute to display "MainThread"
more than "SubThread"
-
the program will compile
and execute but nothing will be displayed
-
What will be displayed when
an attempt is made to compile and execute the following program? The line
numbers are for reference purposes only.
1 2 3 4 5 6 7 8 |
public
class App { public static void main(String[] args)
{ while(true)
{
Thread.sleep(10);
System.out.println("Tick"); }
} } |
-
the program will not
compile
-
the program will compile
but a runtime exception will occur
-
the program will compile
and execute to display "Tick" every ten seconds
-
the program will compile
and execute to display "Tick" every ten milliseconds
-
the program will compile
and execute but nothing will be displayed Printer Friendly Page
Send to a Friend
..
Search here again if you need more info!
|