Escolar Documentos
Profissional Documentos
Cultura Documentos
Handout 8 – Threads
Reference:
For more information on the material covered in this handout, you should take some
time to look at the Sun Java Tutorial, for which there is a link on the intranet, under
the materials listing for this course. See in particular the Threads topic, found in the
Essential Java Classes trail.
e-books on the Intranet – look for the section on applets in any of the Java books e.g.
• Teach Yourself Java in 21 Days (Books Macmillan)
• Java Developers Reference (Books Macmillan)
• Exploring Java (Books OReilly)
1 Overview
This handout introduces the concept of threading in programs. In Java, unlike other
languages such as C, threads have been built-in to the language so that it is easy for a
programmer to write a multi-threaded program.
However, the programmer should have a good understanding of threads and what it
means to create a multi-threaded program.
2 What is a thread?
A thread is a single sequential flow of control within a program – or a single thread of
execution within a program. A program can have multiple threads that are executing at
the same time.
Threading is used in programs to ensure that different tasks can execute
independently, without having to wait for other, non-related, tasks to complete. In
other words, threading allows you, the programmer, to make your programs do many
tasks at the same time.
This is similar to the way in which an operating system, such as Windows, can have
many different applications running at the same time – for example, you could start a
query in Access or SQL Server, and while it is running, switch to a Word document
and type text into it.
Some programs execute using a single thread – this is the case if a Java program is not
written to use multiple threads. If a program is written to use multiple threads, the
path of one thread through the program can be different from the paths of other
threads. For example, thread A might execute the if part of an if-else statement, while
another may execute the else part – this may be due to different inputs for each thread.
Consider a normal stand-alone Java program – to run the program, the interpreter
executes the main method of a class in the application.
Page 1 of 19
8966484.doc FBE – Computer Science Dept
This begins a thread for that program – the program is executed in the order defined
by the code. This order is predefined for a given set of inputs.
If threading is used in the program, the order of execution may be different for
different threads in the program.
// ThreadDemo.java
class ThreadDemo
{
public static void main (String [] args)
{
MyThread mt = new MyThread ();
mt.start ();
//for loop that prints out all the integers from 0 to 50, and
prints the square of each value
for (int i = 0; i < 10; i++)
System.out.println ("i = " + i + ", i * i = " + i * i);
}
}
System.out.print ('\n');
}
}
}
Figure 1 – source code for ThreadDemo and MyThread classes
The ThreadDemo class creates an object of type MyThread. When the ThreadDemo
class is run (it has a main method), the first thing it does is to start a new thread that is
associated with the MyThread object. The program then outputs from the for loops in
Page 2 of 19
8966484.doc FBE – Computer Science Dept
both ThreadDemo and MyThread. But….the output lines of the two classes are mixed
together – it does not complete one before starting the other.
This is because there are two threads running – one to execute the main method of
ThreadDemo and one to execute the run method of MyThread.
Each thread runs independently of the other – each gets resources from the processor
as it needs them. The output from running ThreadDemo might look like that in Figure
2 below. In fact, each time the program is run, the order of the output may be different
– because it depends on when each thread gets the resources it needs.
Threading can be used in stand-alone Java applications (run by the main method) and
can also be used in applets.
Page 3 of 19
8966484.doc FBE – Computer Science Dept
Applets often implement threading to avoid delays between things happening on the
screen – for example, in animations.
The event listener model for applets uses threads – when an event is detected (e.g. a
mouse click) the event is handled by a separate thread. If the handler needs to carry
out tasks that may take some time, the handler should hand off those tasks to another
thread – otherwise subsequent events requiring handling would have to wait for those
tasks to complete.
Multi-threaded programs can also take advantage of multiple processors. There are
many more multi-processor PCs available now than there used to be. If a program has
multiple threads, each thread can run on a separate processor. This is because a thread
has its own call stack (this is what the interpreter uses to keep track of what methods
have been called and in what order).
For example, a calculation that requires one hour on a single-processor machine
could, in theory, run in half an hour on a dual-processor machine or in 15 minutes on
a four-processor machine.
The example in section 3 above has a class MyThread that extends the
java.lang.Thread class. The diagram in Figure 3 below shows the class hierarchy –
MyThread inherits from the Thread class, which implements the Runnable interface.
java.lang.Thread implements
java.lang.Runnable
run()
run()
start()
…
extends
MyThread
run()
Figure 3 – class hierarchy showing the Java Thread class and Runnable interface
The Thread class has two methods that we have seen already (there are more, which
we will cover later) – a run and a start method. When the thread is started (by calling
the start() method), the run() method is executed. The run() method on the Thread
class does nothing – it is empty. A class that extends Thread usually overrides run()
with something that the class needs to do. Any Java statements can be used here.
Page 4 of 19
8966484.doc FBE – Computer Science Dept
For example, in the code examples, the SimpleThread class looks like this:
Page 5 of 19
8966484.doc FBE – Computer Science Dept
The Runnable interface also has the run() method – a class that implements Runnable
has to implement the run() method. A class that implements Runnable can run as a
thread.
If the class extends some other class, it can implement Runnable rather than extending
Thread. This is the case for an applet – an applet is always a sub-class of Applet or
JApplet, so if the applet needs to run as a thread, it should implement Runnable (see
the code example Clock.java).
The class can then be passed to the constructor for a Thread – because the Thread
constructor requires a parameter of type Runnable.
For example, the code example Clock.java is an applet that displays the time,
updating every second. The class implements Runnable. The start method of the
applet creates a new Thread object, using the constructor that takes a Runnable object
and a name as parameters. This is highlighted in bold in the extract from the code
shown below.
Page 6 of 19
8966484.doc FBE – Computer Science Dept
Sometimes, the run() method has some kind of infinite loop running until the thread is
stopped by some external condition. This is often the case in an applet. For example,
in the Clock applet, the run() method repaints the applet every 1000 milliseconds. The
repaint is inside a while loop that checks the condition 'clockThread = = myThread'.
MyThread is the current thread under which the applet is running. The start() method
of the applet has already set the clockThread to be the applet itself, so the while
condition is always true while the applet is running.
The while condition will become false when the applet's stop() method is called
(when the browser leaves the web page) – because the stop() method sets the
clockThread reference to be null – so it is no longer the same as the current thread.
When the while loop finishes, there are no more statements in the run() method, so it
returns – thus stopping the thread. If the browser returns to the web page, the applet is
started again, resulting in a new thread starting up.
Page 7 of 19
8966484.doc FBE – Computer Science Dept
The technique shown above – setting some flag and using that as an exit condition for
the run() method – is better because the thread is stopped at a natural point and there
is less, or no, risk of data being left in a bad state.
6 Thread Life-Cycle
6.1 Thread States
The diagram in Figure 7 below shows the life-cycle of a thread and the different
states a thread can be in during its lifetime. It shows also what methods can cause the
thread to change state. The different states are shown in bold boxes – New Thread,
Runnable, Not Runnable, Dead.
Thread is running…
start() sleep()
When the thread object is created, it is in the New Thread state. For example:
clockThread = new Thread(this, "Clock");
The above line of code creates a new thread from the Clock applet class.
In this state, it is an empty thread for which no resources have yet been allocated. The
only method that can be called on the thread at this point is start().
The next line of code starts the thread:
clockThread.start();
The thread is now running – and it has entered the Runnable state. While the thread
is running, it can switch between two states – Runnable and Not Runnable. If the
sleep() method is called, the thread becomes Not Runnable.
When a thread is in the Not Runnable state, it does not run, even if processor
resources become available for it to use.
When the thread's run() method returns (due to a natural end or an exit condition
being fulfilled) or if the thread is stopped (by its stop() method – not recommended,
this is deprecated in newer versions of Java) the thread enters the Dead state.
Page 8 of 19
8966484.doc FBE – Computer Science Dept
The state of a thread can be checked with the isAlive() method of the Thread class.
This method returns true if the thread has been started and has not yet stopped – it is
in either the Runnable or Not Runnable states.
The method returns false if the thread is in the New Thread or Dead states.
It is not possible to determine if the thread is Runnable/Not Runnable or in the New
Thread/Dead states.
When a thread is created, it inherits its priority from the thread that created it. If the
priority for a thread is not specified, it defaults to the constant value
Thread.NORM_PRIORITY, which is 5.
The priority of a thread can also be set using the setPriority() method. The value of
the priority can be accessed using getPriority().
When multiple threads are ready for execution, the VM chooses the runnable thread
that has the highest priority and executes it. Other, lower priority, threads will not be
executed until this thread stops or becomes unrunnable.
If two threads with equal priority are ready for execution, the scheduler chooses one
of them to run. The chosen one will run until it stops or becomes unrunnable, or until
a higher priority thread becomes runnable. The scheduler is preemptive – this means
that any time a thread with a higher priority becomes runnable, it will be chosen for
execution.
Page 9 of 19
8966484.doc FBE – Computer Science Dept
For example, if the priority of the second thread in the ThreadDemo class is set to the
maximum priority, it will always complete before the original thread executes. The
priority can be set by adding in the line shown in bold below:
On a system that does not do time-slicing, you can use the yield() method in threads
to make sure that one thread does not get all the resources until it completes.
Using yield makes a thread 'well-behaved'.
Another situation in which a thread may become Not Runnable is when it is waiting
for some other resource to be available, for example, data in a file or a database.
This often happens when a thread is sharing data with other threads and has to
consider what the other threads are doing with the data. In the same way that a DBMS
has to manage simultaneous data updates from different clients, a Java program may
need to handle simultaneous access to the same resource.
If the access of each thread to the common resource is not synchronized in some way,
the consumer may not read all the data or key events from the file or queue.
A simple producer-consumer scenario is depicted in the diagram below. The two
threads are running concurrently, competing for the same resources.
Page 10 of 19
8966484.doc FBE – Computer Science Dept
Time
Put Item1
Get item
Put Item2
Get item
Put Item3
In order to ensure that the Consumer gets all the items, in the correct sequence, the
access to the box by both threads must be synchronised – they must be timed in such a
way that the Producer waits for the Consumer to get the last item before it puts in a
new item. The sequence should be something like this:
We can extend this further to say that the Consumer should not attempt to get an item
unless it knows the Producer has finished putting it in. So the Producer needs to also
notify the Consumer:
Page 11 of 19
8966484.doc FBE – Computer Science Dept
The main ideas here are that the Producer and the Consumer threads cannot be
simultaneously accessing the box and that each one must tell the other when it has
completed its action.
Each thread has to get a lock on the box when it accesses it – while the lock is in
place, the other thread cannot access the box. Each thread also has to notify the other
when the item is available (Producer to Consumer) or has been taken (Consumer to
Producer).
At runtime, whenever control enters the synchronized block or method, the thread that
is calling it locks the object whose code block or method is being called.
When an object is locked, no other threads can call any synchronized code on the
object (other threads can call non-synchronized blocks/methods of the object).
When control passes from the synchronized block or method, the lock is released – so
other threads can call the code.
The Java interpreter automatically locks and releases locks in this way.
In Java, waiting and notifying can be carried out using the Object super class methods
wait() and notifyAll(). These methods, and some variations on them, are described in
the table below.
Page 12 of 19
8966484.doc FBE – Computer Science Dept
The code for the Box class below shows how synchronized methods and the wait()
and notifyAll() methods are used together to make our Producer-Consumer scenario
work the way we want it to.
In this class, the item is an integer that is stored in the instance variable contents.
Note that it uses a boolean flag, available – this is true when an item has been put into
the box by the Producer but has not yet been taken by the Consumer. It is false when
the item has been taken but a new item has not yet been put in.
The Producer calls the put() method to put in an item, setting the available flag to true.
The Consumer calls the get() method to get an item, setting the available flag to false.
Both the put() and get() methods use a while loop that checks the available flag. In
this way, the Producer can wait for the Consumer to take the last item, and the
Consumer can wait for the Producer to put the next item.
class Box {
private int contents;
private boolean available = false;
The Producer and Consumer thread classes are shown below. The run method for each
thread calls the put() or get() method, as appropriate. The ProducerConsumerTest
class has a main method that creates a Producer and a Consumer and starts each
thread.
Page 13 of 19
8966484.doc FBE – Computer Science Dept
this.number = number;
}
class ProducerConsumerTest {
public static void main(String[] args) {
Box b = new Box();
Producer p1 = new Producer(b, 1);
Consumer c1 = new Consumer(b, 1);
p1.start();
c1.start();
}
}
Figure 10 – the Producer and Consumer thread classes and a class to test the scenario
8 Daemons
[Code Examples: Threads/DemoDaemon.java]
A Daemon is a special kind of thread. If a daemon thread is running as part of a
program, the program can terminate without waiting for the daemon to finish
whatever it is doing.
Normal (non-daemon) threads must complete before the program can terminate – this
means their run() methods must return.
Page 14 of 19
8966484.doc FBE – Computer Science Dept
Therefore, daemon threads are useful for running background tasks that should run
continuously while the program runs. For example, a thread that monitors the
availability of system resources can be left to run in the background and can just stop
whenever everything else in the program is complete.
To make a daemon thread, call the setDaemon() method – it takes one boolean
parameter – if it is true, the thread becomes a daemon.
This method throws an IllegalThreadStateException, so this exception needs to be
caught in the code.
{
public DemoThread(String name)
{
super(name);
}
The following class has a main method that starts both threads. If both threads were
normal, non-daemon, threads, the program would go on indefinitely – because of the
infinite while loop in the run() method of the DemoDaemonThread class. But,
because it is a daemon, the program terminates when all other threads have
completed.
Page 15 of 19
8966484.doc FBE – Computer Science Dept
class DemoDaemon
{
public static void main (String[] args)
{
DemoDaemonThread dt = new DemoDaemonThread("Daemon Thread");
DemoThread nt = new DemoThread ("Normal Thread");
dt.start();
nt.start();
}
You can check if a given thread is a daemon using the isDaemon() method of Thread:
9 Timers
[Code Examples: Threads/Reminder.java, ReminderBeep.java]
Writing your own threads in the ways discussed in this handout can be complex and
difficult if there are many tasks to be handled.
To make the life of a programmer a bit easier, Java 1.3 introduced the java.util.Timer
and java.util.TimerTask classes. These can be used to run repetitive tasks, such as
monitoring.
The TimerTask class implements the Runnable interface, so it must override the run()
method.
A Timer object can schedule one or more TimerTask objects to be executed one time
or a number of times by a background thread. The tasks can be scheduled to run at a
time or times in the future or after a specified delay period.
TimerTask is abstract, so you extend it to define the tasks that need to be carried out.
For example, when the Reminder class below is run, it displays the output:
About to schedule task.
Task scheduled.
The uses of the Timer and TimerTask classes are highlighted in bold.
import java.util.Timer;
import java.util.TimerTask;
/**
* Simple demo that uses java.util.Timer to schedule a task to
execute
* once 5 seconds have passed.
*/
Page 16 of 19
8966484.doc FBE – Computer Science Dept
Each task that you want the timer to run must be defined in a sub class of TimerTask.
The run() method defines what the task does.
A Timer object is instantiated and each task is scheduled using one of the schedule
methods (see the table below for the different versions). One Timer can schedule
multiple tasks – but all the tasks run as part of the same thread.
The program will keep running as long as any Timer threads are running, so the Timer
thread(s) must be stopped somehow. One way to do this is to call the cancel() method
of the Timer object, as is done in the RemindTask above:
timer.cancel();
If this call were not made, the program would keep running because there is a live
thread running (the Timer object).
Alternatively, the Timer object can be created as a daemon thread. The Timer has a
constructor that takes one boolean parameter – if it is true, the Timer object will be a
deamon thread. For example:
However, for this example, the main method finishes after the line
System.out.println("Task scheduled.");
At this point, the program has finished executing; it has a thread (the Timer) but the
thread is a daemon, so the program terminates before the 5 seconds have elapsed for
the RemindTask to run.
However, there could be other threads running that stop the program from
terminating, even if the Timer thread is cancelled. If any of the AWT classes are used,
AWT automatically creates a non-daemon thread that keeps the program alive.
Page 17 of 19
8966484.doc FBE – Computer Science Dept
In this case, another way to tell the program to end all running threads and terminate
is to call System.exit(0).
See the code example ReminderBeep.java for an example of this (it uses the
java.awt.Toolkit class to make a beep sound in the TimerTask object).
The different schedule methods of the Timer class are shown in the table below. See
the code example ReminderManyTimes.java for an example of using the
schedule(TimerTask task, long delay, long period) method.
10 ThreadGroups
If you are working with a multi-threaded program, you can group your threads into
named groups. This allows you to carry out actions, such as setting priority and
setting as a daemon on each group of threads.
This is done using the ThreadGroup class.
For example, if you want to add your thread to a different group, you can do
something like the following:
Page 18 of 19
8966484.doc FBE – Computer Science Dept
At this stage, it is ulikely that you will need to group threads – it is usually sufficient
to allow the Java VM to handle all threads in the main group.
There are various methods on the ThreadGroup class that can be invoked to get
information on or change properties of threads in the group.
Page 19 of 19