Multiprogramming
Mainframes were designed to allow several users
to use the computer at the same
time. Each user's process would have it's own
memory area or address space to
hold data and instructions. The central processor
would then do computations a bit
at a time on each user's program in sequence.
This provided the illusion that several
things were being done at the same. The old name
for this was 'multiprogramming'
though the terms time sharing, multitasking and
multithreading were also used.
Multitasking
Today multitasking is used to describe the low-level
creation of separate processes
with their attendant divisions of memory. An
example of this is running several DOS
sessions simultaneously. Multitasking is defined
at the system level. The system is
comprised of the combination of operating system
and the hardware platform that
the OS is running on. For example, a platform
might be a version of Linux running
on an Intel processor. Because there are a lot
of the systems resources involved in
setting up new processes, and there is a lot
of work that needs to be done to switch
between the context of each process, mutltitasking
is referred to as a heavyweight
process. Multitasking is also called timesharing
as each process would run in turn for
a couple milliseconds.
Multithreading
Multithreading is now reserved to describe creating
many threads of activity within
a single multitasking process. Multithreading
is a newer phenomena than multitasking
and came out of experimentation that was going
on in the mid-seventies. A thread API
standard was described in 1995 for POSIX, ( the
IEEE's Portable Operating System
Interface for UNIX). The same year Java was released
with multithreading support
built in. James Gosling gives credit for the
ideas behind his Java thread model to the
early work done by C.A.R. Hoare and also to the
implementation of threads that was
provided in the Cedar and Mesa languages developed
at Xerox PARC.
Because in multithreading the multiple activities
are occurring within a process, not
as many extra resources need to be allocated
so multithreading is also referred to as
a lightweight activity. When we discuss multithreading
in Java we are talking about
creating many lightweight threads of activity
within the same process
Multithreading Uses
One of the key advantages to multithreading is
it allows several things to occur in
a user's program at the same time. For instance,
a download that is managed in a
separate thread will not not interfere in a graphical
user interface continued operation.
Music can be playing in the background in another
thread while the GUI remains
responsive and the download is going on.
Multithreading threading is also useful to solve
problems that can better be done by
dividing the labor into parallel processes. Consider
a hypothetical phone directory that
has entries for all the people in North America.
An old algorithm called divide and
conquer involves testing if a name is in the
upper or lower half of the book. Then the
process is repeated on each subsection until
the name was found. The problem with
our book is traversing the list might take a
long time. Another approach might be to
divide the book into perhaps a hundred divisions
and have each thread divide and
conquer it's subsection. (The optimal number
of thread would need to be determined
as starting too many threads can start to slow
the system down.
Another use for multithreading is that some programs
are easier to write and implement
using threads. An example of this is a server
program. Having a server spawn a new
thread for each connection allows it to deal
with several clients simultaneously. It should
be said again spawning too many threads may not
be good as it can take a toll on the
system's performance.
The Java Language Environment
James Gosling & Henry McGilton
http://java.sun.com/docs/white/langenv/index.html |
Java's threads are pre-emptive, and depending on platform on which the Java interpreter executes, threads can also be time-sliced. On systems that don't support time-slicing, once a thread has started, the only way it will relinquish control of the processor is if another thread of a higher priority takes control of the processor. If your applications are likely to be compute-intensive, you might consider how to give up control periodically by using the yield() method to give other threads a chance to run; doing so will ensure better interactive response for graphical applications. . |
Preemptive and Time-Sliced Scheduling
When a Thread object's start( ) method is called
thread execution begins. Calling
start( ) registers the thread with the thread
scheduler making the thread eligible to
run on the CPU. The thread may then enter a competition
with other threads for
CPU time.
There are different models which are used to schedule thread activity.
The two most
popularly implemented models are preemptive and time-sliced. Preemptive
multithreading
assigns each thread a priority and threads with higher priority preempt
threads with lower
priority with access to the cpu. The preemptive model affords some
predictability with
respect to which processes will be allowed more access to the cpu.
Time sliced scheduling is also called round-robin scheduling and divides
the time evenly
between the different threads that have been started so each has equal
time with the cpu.
Round-robin scheduling guards against one thread monopolizing the central
processors time.
Java schedules preemptively, however this behaviour
is superimposed on the type of
scheduling that is incorporated in the system
that the JVM is running on. Some Windows
platforms use round robin scheduling, so the
result is a hybrid of preemptive and round-
robin behaviour. Behaviour may vary if an threaded
application is ported across several
platforms. Accordingly it is not recommended
you trust scheduling behaviour to derive
any kind of strictly predictable coding results.
Java threads have priorities assigned to them.
The default has the int value of 5 and has
the name, NORM_PRIORITY. MAX_PRIORITY
is
10, and MIN_PRIORITY is 1.
The Thread class has set and get methods to set
priority or learn what the priority is for
a given thread.
Example
t1.setPriority( t1.getPriority( ) +1 ); //
increments a thread's priority by one
ThreadGroup class
Threads can be grouped into sets so they can be
called on a single method invocation.
Thread grouping provides a mechanism to coordinate
thread activity. A thread group
can also include other thread groups forming
a tree where every thread group except the
initial thread group has a parent. A thread can
access information about its own thread
group, but not it's thread group's parent thread
group or any other thread groups. The
class has a version of interrupt( ) to stop all
the threads in a group at once.
Example ThreadGroup
tg = Thread.currentThread( ).getTheadGroup( );
How many Thread?
Each thread has a default stack size of 400K.
Peter Vander Linden in his text 'Just
Java' reported using a 32-bit Unix machine with
2GB user address space was able
to create 2000 'do nothing' threads on the machine
before it quit.
Thread States
Threads exist in a number of states during their life cycle. First there
is the running
state. This is the condition where a thread has control of the central
processing unit.
Then there are a variety of wait states. A number of thread methods
cause a thread
to go into the wait state. In this state the thread is not seeking
the attention of the cpu.
The thread methods that put a thread into this state include wait(
), sleep( ) and yield( ).
(The suspend( ) method has been deprecated so we don't count it. )
The blocked
condition is not caused by a method call. Instead, it is a behaviour
that saves on cpu
cycles. The read( ) methods of the IO classes go into a blocked state
if there is nothing
available to read. The final state is the dead state. When a run method
of a thread is
completed, the thread is in the dead state. In the dead state, though
execution is
completed the methods of that thread object remain available to be
called.
Thread States
Running | - in the running state a thread is in control of the cpu |
Wait states | - caused by wait( ), sleep( ), yield( ) or the blocked behaviour |
Ready | - not waiting for anything except the CPU |
Dead | - a thread's run method has completed |
(If you don't mind a baseball analogy, running
is like being 'up to bat', ready is 'on deck'
and wait states are like being on the bench.
In the premptive model the the bench is like
a 'free for all' where pushier players or couch
favorites represent threads with higher
priority. They get back up to bat first regardless
of the wait. Round-robin scheduling
is like the coach that is painfully fair. Every
player goes in turn regardless if it will result
in the loss of the game. A dead thread is like
a player that finishes his turn at bat for the
last time in the game and has left the dug out.
The Dead State
When run( ) returns the thread is dead. Dead threads
are unusual conceptually. Though
they are no longer active their methods can still
be called. It use to be that to kill a thread
the stop( ) method was called. A lot of old texts
will show examples of the stop method
being called on a thread. Unfortunately there
were serious problems with the stop( )
method and a few others so they are now deprecated.
The recommendation now is to
decommission a thread is to first call interrupt
on it then set it's reference to null.
Example
some_thread.interrupt( );
some_thread = null;
The following table excepts from the API the explanation why stop( ),
and the suspend
and resume( ) methods were deprecated.
Major
Thread Method Deprecations //
excerpted from JDK API documentation
void stop( ) -deprecated - inherently unsafe. Stopping a thread with Thread.stop causes it to unlock all of the monitors that it has locked (as a natural consequence of the unchecked ThreadDeath exception propagating up the stack). If any of the objects previously protected by these monitors were in an inconsistent state, the damaged objects become visible to other threads, potentially resulting in arbitrary behavior. Many uses of stop should be replaced by code that simply modifies some variable to indicate that the target thread should stop running. The target thread should check this variable regularly, and return from its run method in an orderly fashion if the variable indicates that it is to stop running. If the target thread waits for long periods (on a condition variable, for example), the interrupt method should be used to interrupt the wait. void suspend(
) -deprecated.
as it is inherently deadlock-prone. If the target
resume(
) -deprecated. -exists solely for use
with suspend( ), which has been
For more see 'Why are Thread.stop, Thread.suspend and Thread.resume Deprecated?.',-@ Sun |
The Thread class
Java was designed on a foundation which provided
support for threads. Java depends
on a background thread called the garbage collector
to automatically deallocate unused
memory. Java also assigns the management of painting
graphical components and handling
the events they generate to a separate thread
called the event thread (aka the AWT thread).
Java supplies these lightweight processes called
thread via instances of the Thread class.
The Thread class supplies a set of methods to
manage the life cycle of any threads the
developer creates. In Java a thread is an instance
of the Thread class. Thread is in the
java.lang package and so does not require any
explicit package imports.
A thread object can be thought of as a virtual
central processing unit or cpu. In this analogy
the thread's start method serves to turn the
cpu on. The run method is to the thread process
what main is to a java program running in a command
line process. The run method supplies
the point of entry for the activities that will
be executed by the thread. Any number of such
threads of activity can be created within the
practical limits of the systems memory and
performance characteristics.
A Table Comparing Threads Parts
to a Java Virtual Machine running an Application in main( )
Virtual Machine | Thread |
central processing unit | Thread object |
power on | start( ) method |
main( ) method | run( ) method |
Details of Thread Actiivity in the Java Virtual Machine
Each Java application runs in a single instance
of the Runtime class. Runtime
defines methods like exec( ) and exit( ). The
exit( ) method initiates a shutdown
sequence for the current runtime object. This
method normally never returns.
The conventional way to call the method is via
System.exit(0) where the 0 int
value is a status code that indicates a normal
exit. Non-zero status codes indicate
abnormal halts.
Aside from the background threads such as the
garbage collector and the event
thread when, the Java Virtual Machine starts
a single non-daemon thread calls
the main( ) method of the class that has been
supplied as a parameter to the java
runtime command. This thread and any others that
are started will run until one of
two conditions occur. Either the exit( ) method
is called in the runtime environment,
(and the security manager has permitted the exit
to happen), or all the non-deamon
threads have died. The non-daemon threads will
have died either by having their
run( ) methods return or by throwing an exception
that propogates beyond the name
scope of the run method.
Thread Support
Most thread support resides in class Thread. There
is a special kind of support
that allows thread activity to be coordinated
and communicating that resides in
the Object class. There is also some additional
help supplied by the runtime
environent. Exception propagation in a thread
instance is limited to the thread
so a problem in one thread will not to interfere
with the activities of other
processes. Thead class has the following declaration.
public class Thread extends Object implements
Runnable //
Thread implements Runnable
The Runnable Interface
The Runnable interface defines the single method
run as shown in the interface
declaration. When you override it it is declared
public, returns void and takes no
arguments. It is inside the run( ) method that
the work that is being given to the
thread is contained.
public interface Runnable{
public abstract void run( );
}
Two Approaches To Running Threads
There are two approaches to creating threads.You
can either extend Thread
class and thereby inherit the Runnable behaviour.
or you can implement
Runnable in a class to obtain thread behaviour.
These two approaches can
be summarized as
1) Subclass Thread and override run( )
2) Target another object that implements Runnable
Extending Thread has the advantage of making it's
methods readily available for use
in the subclass.This isn't so when implementing
Runnable. There is a work-around.
A reference to the currently executing thread
can be obtained via the static call on
the method currentThread( ) from inside run(
). Then Thread class methods can be
called using this reference.
Example Thread current = Thread.currentThread( )
Extending Thread is not always possible because
of java's single inheritance model.
For instance, applets extend Applet class therefore
cannot also extend Thread. An
applet's only choice is to implement Runnable.
First consider the subclassing technique.
Subclass Thread
In summary the steps involved are as follows.
1) extend Thread
2) override run( ) in the subclass
3) create an instance of the subclass and call
start( ) on it. //
start calls run( )
Subclassing Thread Sample Code
class BlueBird extends Thread{
public void
run( ){
System.out.println("BlueBird ");
}
}
class Birder{
public static
void main(String[]args){
BlueBird
blue = new BlueBird( );
blue.start(
);
}
}
// in case you don't override
run the Thread class supplies a do-nothing version
Call another objects run( )
The second approach involves creating a class that implements the Runnable
interface. This class is instantiated and supplied as an argument to
a Thread
class constructor. This is summarized in the following steps.
1) Create a class which implements Runnable
2) Provide an instance of Runnable to a Thread
constructor
For this approach, the thread constructor with
the following form is used.
The Runnable target instance provides the implementation
of the method
Example public Thread(Runnable target)
Code Sample Showing a Runnable Object supplied to the Thread Constructor
class TargetClass implements
Runnable{
public void run( ){
System.out.println
("Thread takes target and starts it's run");
}
}
class TargetRun{
public static void main(String[]args){
TargetClass
target = new TargetClass( );
Thread
arrow = new Thread(target);
arrow.start(
);
}
}
Key Thread Class Methods
yield( )
yield( ) is a static method which moves the executing
thread to ready state where
it waits or re-executes. Calling yield( ) at
regular intervals ensures a single process
doesn't monopolize the CPU, permitting other
threads to execute. To ensure the
thread is checked occasionally, the time-consuming
thread to set to a slightly lower
priority then the thread that needs to be checked
occasionally and yield( ) is called
periodically to let the occasional thread to
run.
sleep( )
A static method, which causes the currently
executing string to 'time-out' for an approximate
time specified in milliseconds, or milliseconds
and nanoseconds Time is approximate as the
thread re-enters the ready state and there is
no guarantee when it will run again. sleep( )
throws InterruptedException //
As long as the exception is not thrown the method guarantees
a minimum amount of sleep time.
suspend( ) [deprecated] A thread receiving
a suspend call stays suspended until it receives
a resume( ) call. A thread can be suspended by
itself or another thread but only resumed by
another thread so a suspend( ) followed
by resume( ) in the same run method will never
resume( ). resume( ) called
on a thread that is not suspended has no effect
interrupt( )
To causes a thread to abandon a process such
as an I/O operation interrupt( ) can be
called. Calling interrupt( ) causes an InterruptedException
to be thrown. You can
catch this exception to implement a 'time-out'
in a process. For example,
catch(InterruptedException ie) {
break;
}
join( )
This method causes the current thread to stop
until the thread (on which the method was
invoked) is finished. This method provides a
rudimentary form of communication between
threads as one thread is allowed to complete
before the next thread is started.
blocking
Blocking is not a method, but a behaviour.
when forced to wait on some external factor, the
thread steps out of the running state and is
said to be blocked. For instance, when a method
has to wait to read from a socket, the method
will 'try' to read a byte immediately. If none is
available, rather than tying up the running state,
the method 'steps out' of the way and is said
to be blocked (All java IO methods are
designed to behave this way).
setDaemon( )
This method makes a thread a daemon versus a
user thread.
daemon //
around Waterloo-Kitchener it's most often pronounced day - mon
Pronounced "demon." A UNIX program that executes in the background ready to perform an operation when required. Functioning like an extension to the operating system, a daemon is usually an unattended process that is initiated at startup. Typical daemons are print spoolers and e-mail handlers or a scheduler that starts up another process at a designated time. The term comes from Greek mythology meaning "guardian spirit." the TechEncylopodia |
In Java a daemon thread is a service provider
whose value is predicated on the presence
of a user thread. The JVM can only exit if zero
user threads are running. If you want a
thread to run but not prevent a user from exiting
the JVM, making the thread a daemon
thread will accomplish this.
Thread Code Sample
The following example shows that when the threads have equal priority
they more
or less get even turns 'at bat'. The first thread to get started gets
a few turns in first.
If you uncomment the priority setting methods you can see how you can
wait the
priority so one gets it's work done ahead of the other.
class Busy extends Thread{
public void run(){
for(int i=0;i<20;i++)
System.out.print("-Busy,Busy,Busy!-");
}
}
class Occasional extends Thread{
public void run(){
for(int i=0;i<20;i++)
System.out.print("-Occasional!-");
}
}
class RunBoth{
public static void main(String[] args){
Busy b=new Busy();
Occasional o=new Occasional();
// b.setPriority(7);
// o.setPriority(4);
b.start();
o.start();
The Thread Class
Fields
static int MAX_PRIORITY | The maximum priority that a thread can have. |
static int MIN_PRIORITY | The minimum priority that a thread can have. |
static int NORM_PRIORITY | The default priority that is assigned to a thread. |
Constructors
Thread ( ) | Allocates a new Thread object. |
Thread (Runnable target) | Allocates a new Thread object. |
Thread
(Runnable target, String name) |
Allocates a new Thread object. |
Thread (String name) | Allocates a new Thread object. |
Thread
(ThreadGroup group, Runnable target) |
Allocates a new Thread object. |
Thread (ThreadGroup group, Runnable
target, String name) |
Allocates a new Thread object
so that it has target as
its run object, has the specified name as its name, & belongs to the thread group referred to by group. |
Thread (ThreadGroup group, String name) | Allocates a new Thread object. |
Methods
static int activeCount( ) | Returns the current # of active threads in this thread's thread group. |
void checkAccess( ) | Determines if the currently
running thread has permission to modify
this thread. |
static Thread
currentThread( ) |
Returns a reference to the currently executing thread object. |
void destroy( ) | Destroys this thread, without any cleanup. |
static void dumpStack( ) | Prints a stack trace of the current thread. |
static int enumerate
(Thread[] tarray) |
Copies into the specified
array every active thread in this thread's
thread group and its subgroups. |
ClassLoader
getContextClassLoader( ) |
Returns the context ClassLoader for this Thread. |
String getName( ) | Returns this thread's name. |
int getPriority( ) | Returns this thread's priority |
ThreadGroup
getThreadGroup( ) |
Returns the thread group to which this thread belongs. |
void interrupt( ) | Interrupts this thread. |
static boolean
interrupted( ) |
Tests whether the current thread has been interrupted. |
boolean isAlive( ) | Tests if this thread is alive. |
boolean isDaemon( ) | Tests if this thread is a daemon thread. |
boolean isInterrupted( ) | Tests whether this thread has been interrupted. |
void join( ) | Waits for this thread to die. // 1of 3, also specify milli /& nano |
run( ) | If this thread was constructed
using a separate Runnable run
object, then that Runnable object's run method is called; otherwise, this method does nothing and returns. |
void setContextClassLoader
(ClassLoader cl) |
Sets the context ClassLoader for this Thread. |
void setDaemon(boolean on) | Marks this thread as either a daemon thread or a user thread. |
void setName(String name) | Changes the name of this thread to be equal to the argument name. |
void setPriority
(int newPriority) |
Changes the priority of this thread. |
static void sleep
(long millis) |
Causes the currently executing
thread to sleep (temporarily cease
execution) for specified milliseconds.// 1 of 2 also ( millis, nanos) |
void start( ) | Causes this thread to begin
execution; the Java Virtual Machine
calls the run method of this thread. |
String toString( ) | Returns a string representation
of this thread, including the
thread's name, priority, and thread group. |
static void yield( ) | Causes the currently executing
thread object to temporarily
pause and allow other threads to execute. |