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( );
}
}
We might also
mention we looked at various methods of
stopping threads including throwing the InterruptedException.
Threads allow a
program operating on an operating
system to execute separate
activities
concurrently. A
system thread is a sub-process of the operating system
that can be
scheduled. Threads are important in many
server applications where
thread resources
can be
managed to supply services to many clients concurrently.
Java Uses Each Operating
System's Unique Thread Model
Each
operating
system's thread implementation is different.
Before Java
appeared , a programmer
would usually need
to
cater to the thread model used on the operating system
that he or she
was working
on.
Java supplies a
generic thread model that is portable across
operating
systems
freeing the developer of needed to know
about the details of each operating
systems
thread model.
//
Java's thread model is portable
As we discussed
earlier, this does not imply that Java thread
behavior
will be identical
on every operating system. Cliff
Berg in his book, 'Advanced Java',
brings some
interesting
information to bare on these issues.
Java Thread Objects
are Wrappers for Native Threads
Java
implementations
usually make
use of the native
thread model. The Java thread object is an abstraction
that uses
the native system thread implementation. Java
Thread class objects
act as 'wrappers'
for the native
threads of the operating system.
Details of the start( ) Method
When a Thread
object is instantiated, the native thread
doesn't exist
yet. Only the
Java wrapper has been
allocated in memory. When the start( ) method
is called,
a
system thread is started and run. The start( ) method
acts as a bridge
interfacing
the Java object to native
thread sub process. The start( ) method provides
the
data structure that connects the native system thread
to the Java thread
object,
and creates and runs the
native system thread.
Relationship Between Java Thread, start( )
& Native Thread
Peter Linden in
'Just Java' provides a good classification
system for
threads based
on how they interact. The four
general categories
are described below.
Relationships between Threads
1) Unrelated Threads
2) Related but Unsynchronized
3) Related and Synchronized
4) Related, Synchronized and Cooperating
Unrelated Threads
We see threads that unrelated in use all the time
when
we use computer programs.
Modern web browsers use
multithreading extensively.
For instance while a news
clip is being played in one frame, an
application may be
running in another process, perhaps a
time display. At
the same time the browser
might be negotiating a download
and
presentation of a new page in a new tab. A print
job
could
even be going on in the background.
These are all
unrelated but concurrently running
threads.
Example
Related but Unsynchronized
This category can be used to describe threads
that are
cooperating on completing the
same task, however each
thread has a duty that
they can completed independently
of
other threads. A parallel process where a number
range
is split into a number of sub-ranges where
each sub-range
is searched by a
separate thread fits this description.
Peter van der Linden, in his Java text, 'Just Java'
has
a
nice code sample that shows threads working in a
related but unsynchronized fashion. It is very concise as
well. A couple lines were added to handle the case where
a number has not been provided at the command line to
test if it is prime.
The way the program works, is if no factors are found
the program returns with no reports.
Peter van der Linden's TestRange Code // 'Just Java', Sun
Press
class
testRange extends Thread{
static long possPrime;
long from, to;
//
constructor
//
record
the
number
we are to test, and
//
the
range
of
factores we are to try
testRange(int argFrom, long argpossPrime){
possPrime = argpossPrime;
if (argFrom==0) from=2; else from=argFrom;
to = argFrom + 99;
}
public void run(){
for(long i=from; i<=to
&& i<possPrime; i++){
if
(possPrime%i == 0) {
// i divides possPrime exactly
System.out.println(
"factor " + i + " found by thread " +
getName( ));
this.stop ( );
// uses the evil stop( ) method ed.
}
yield( );
}
}
}
public class testPrime{
public static void main(String s[]){
if (s.length<1){
System.out.println("Enter a number to test if it is
prime");
return;
}
long possPrime = Long.parseLong(s[0]);
int centuries =(int)(possPrime/100) + 1;
for(int i=0;i<centuries;i++){
new testRange(i*100, possPrime).start( );
}
}
}
// for example,
3242891 is prime
Related and Synchronized
Related and
synchronized threads are interacting.
Both
are accessing some shared
domain. Perhaps three server
threads have been
created to serve clients concurrently
and these threads are all sharing data from the
same
address space.
//
threads working on same shared data
Imagine a thread
accessing a data item at precisely the
same instance
that another thread is in the process
of
changing that data item. There is a potential
problem
of data being read incorrectly or worse, being lost or
corrupted.
As we will see, special care needs to be
taken
when threads interactively share data.
Related, Synchronized and Cooperating
A higher order of interaction is still possible.
This is the
situation where threads interact
by sharing the same
data but go further. Threads
can communicate to each
other when
they are ready to process each other's data.
This is often described as the Producer
Consumer
model. This kind of communication can
also be done
in Java using classes from the java.io
package
PipedInputStream and PipedOutputStream.
We talk about the latter two cases in the following
discussion where examples will be supplied.
Synchronization is the process where
concurrent
threads
are only permitted to access
shared data in turn, essentially,
in a queue.
A lot of terms have been used to describe this
type of mechanism. For instance, P. Naughton
stated in
'The Java Handbook',
"A monitor is an object which is
used as a mutually
exclusive lock or mutex."
The
Monitor Lock & Mutex
In a short line, three terms have been used,
'monitor', 'lock'
and 'mutex' all of which relate to the
idea of synchronization.
The
term 'monitor' is used not in the sense
of something
visual rather in relation to it's
root
'mono' meaning 'one'.
The 'lock' term
is used in common sense to indicate a
thread
has a lock on a process. The lock implies
other
threads are locked out of using a section
of data. The term
'mutex' is an abbreviation for
the words, 'mutually exclusive'
and again
alludes to the
idea of a 'one at a time' usage.
//
mono
=>
'one',
mutex==>
mutually
exclusive, lock =>
exclusive use
The
Semaphore
Another term used in this context is 'semaphore'. You
may
be surprised
to know that the semaphore is a real object,
a flag that was
passed by railmen to the engineer of a
train.
It was used where trains that ran in both directions shared
a single section of track. The flag physically represented a
guarantee
that they and only they had access to
the single
section of track.
Specialists have stricter
definitions of these terms. The
following quote, found in Heller & Robert's
'The Java
Developers Handbook' offers a stricter
definition of a
monitor.
"A Java class as a whole is not a monitor strictly
speaking
unless all it's non-private methods
are synchronized to
protect all
data accesses and all it's data methods are
marked as private."
From the same source comes the following regarding
the
semaphore flag.
"Before the days of electronic communication,
in places
where a single track was used by trains
in both
directions,
the signal
man would have a flag or semaphore. No train
was
allowed on the track unless the driver
held the flag
obtained from the signal man. When the
train passed off
the track, the
driver returned the flag to the signal man." ...
"When a 1 or 0 is used to protect
delicate data, the
mechanism is often called a binary
semaphore"
- "The Java Developer's Handbook"
- Heller & Roberts
The semaphore
mechanism is built into Java's Object
class. Cooperation
between
synchronized objects is
also made possible by Object class methods
wait( ),
notify( )
and notifyAll( ).
Guarded & Concurrent Reentrancy
Reentrant code is
called 'thread safe' code and describes
code
that can be
executed safely by many threads at the
same time without any extra
provisions for
protecting data.
Cliff Berg in
'Advanced Java' helps outline two forms
of
reentrant
code, 'guarded' and 'concurrent' that brings us
into conformity with
the Object Management Group's UML
language definitions for these terms.
Concurrent Reentrant Code
Concurrent
reentrant code is naturally thread-safe by
virtue of how it has been written in Java and does not
use
any explicit
internal mechanism that sends threads
into the wait state to achieve
concurrency.
This boils down to
avoiding the use of static and instance
variables
in methods
or scoped sections of code unless
these variables are defined as constants. In other words,
use local method variables that have the same life span
as the method call itself.
//
avoid static variables and instance variables defined in
// class scope unless these variables are constants
Instance and static
variables may both outlive the life cycle
of a
section of code
making them vulnerable to another
threads access. Concurrent reentrant
code
is based on the
use of local variables that will
disappear at the same time
the method code completes.
//
concurrent reentrant code use local variables whose life span
// is the same as that of the code
section it is in
Guarded Reentrant
Code // 'code marked synchronized'
Guarded reentrant code introduces
the 'explicit
internal
mechanism' mentioned in the previous
paragraph. In Java
this comes
down to using the 'synchronized' keyword. The
synchronized keyword works
through a mechanism that
translates to VM instructions
at the Virtual Machine
level.
The VM level instructions are used to
allow or restrict
access to
synchronized code depending if that code is
being
used by another thread or
not.
// guarded re-entrant code uses the
'synchronized' keyword, and
// works at the VM level
The 'synchronized' keyword
The synchronized keyword can be applied to static
or
non-static methods or to
blocks of code. When applied
to a block of code,
a 'synchronized
block' is created.
Because the
'synchronized'
keyword is not considered
part of
a method's signature, it can be overridden so
subclass versions of
the synchronized
methods do not
need to be synchronized.
//
the
'synchronized'
keyword
may
be applied to
// 'synchronized' blocks static and non-static methods
Synchronization
makes a
process 'uninterruptable' or
'atomic'. Some processes are atomic naturally
such
as
the assignment of an int value. Assigning a long value
is not however atomic.
An interrupt could potentially stop
a thread before a long assignment completed.
The
synchronized keyword can be used to ensure a section
of code will finish
before
being interrupted by another thread.
This is particularly important
in transaction processing.
//
synchronization makes a process 'atomic' or uninterruptable
Early Synchronization Costs Have Been Cut
Cliff Berg reports
that early versions of synchronization
in Java were
expensive
in terms of performance, being
almost 10 times slower than comparable
non-synchronized
code. Fortunately, in newer versions of Java, the process
of synchronization is only moderately slower than regular
Java methods.
//
performance of synchronized methods has greatly improved
Pre-empted Synchronized Code Retain it's Lock
Synchronized
code
also can be pre-empted in the normal
course of thread scheduling activity.
Java's
model is
designed to let low priority threads get some time even
if
high priority
threads are running. When pre-empted, a
synchronized section of code
retains
it's lock. No other
thread will execute the code while the suspension
of
activity
is occurring.
Synchronization is
also used extensively in Java library
code. Vector uses synchronized methods.
// a
preempted thread retains it's lock on a synchronized section of code
The 'synchronized' keyword is simple to add. The
first
case we look at adds
the synchronized keyword to a
section or scope
of code. Because of the performance
costs associated with synchronization, keeping a
section
of code that is synchronized as short as possible will
keep the performance premium down.
Synchronizing a Block of Code
Following is an example of a synchronized block
of code.
A synchronized block
is a parenthesized code block
prefixed with synchronized( target )
where target is
an
arbitrary class instance. This works as every
root class
descendant inherits
Object class' mechanism to queue
or 'single file'
calling threads.
Parts of a Synchronized Block
synchronized (target_object)
{
/*
perhaps a subset of a method's code*/
}
Following is an synchronized block from a CORBA
code example.
Example of a Synchronized Block
java.lang.Object
sync = new java.lang.Object(
);
synchronized
(sync)
{
sync.wait(
);
}
Synchronizing on an Non-Static or Instance Method
Synchronizing
non-static methods provides a means to make
'thread-safe' accesses to
instance variables. With a non-static,
synchronized
method the implied object that is used for
synchronization is 'this', the
instance object. As a result, each
method is synchronized to it's container. This type of lock is
called an 'object lock'. Note other non-synchronized methods
and static methods of this class are not effected by this lock.
//
'object lock' is a lock on an instance
It should be pointed out that other objects of
the same class
have no relationship
to each other in terms of concurrency.
Each object is only
synchronized to itself. The moment
the
synchronized keyword is added a lock based
on the class
object is created.
//
synchronizing instance methods provides thread safety within the object
Non-Static Method Example
class
Sync{
int
[
]
key
= new int[ 8 ];
synchronized
void
setKey(int
i,
int value){
key[i]=value;
}
synchronized
int[]
getKey(
){
return
key;
}
}
The instance variables can work on a
static variable
as is shown in the following example.
Example
class Synch2{
static
int
key;
synchronized
void
setKey(int
value){
key=value;
}
synchronized
int
getKey(
){
return
key;
}
public static void main(String [] args){
Synch2
sync=new
Synch2();
sync.setKey(6);
System.out.println(sync.getKey());
}
}
How is it Useful to Synchronize Object
Methods
If many objects
used the same instance simultaneously
the data being targeted would be thread safe and
protected from corruption. The instance when used will
behave with thread-safe integrity.
//
consider
several
threads
use the same instance of a
calculator
// class. The methods will not interrupt until completing inside this
// multi-threaded environment
Synchronizing on a Static or Class Method
Synchronizing on a static method is similar in
procedure to
adding synchronization
to an instance method. Instead of
the class
object
being the element that supplies
the lock,
the class itself supplies the mutex. This is called a 'class
lock'.
Static Method Example
class Sync{
static int [ ] key = new int[ 8 ];
static synchronized void setKey(int i, int value){
key[i]=value;
}
static synchronized int[] getKey( ){
return
key;
}
}
Form Equivalent to a Synchronized method
We can synchronize a section of code by
putting it in a
synchronized block inside
a method. If this block occupied
the entire scope
of the method it would be the same
as the
second notation form that is the common
form found in the
Java libraries.
Form
A
void method( ){
synchronized
(this){
//...
}
}
// is the same as
Form B
synchronized
void
method{
//...
}
Race Condition // coordination of thread activity is lost
Race condition occurs when more than one thread
is
able to act on shared data
at the same time before some
condition limiting
their activity is able to be invoked.
There
are a couple of ways to protect against race
condition.
First is to use synchronized code so one
threads actions
are complete before
the next begins. The other technique
involves making shared items static.
Making the variables
class variables
ensures there is only one item being
effected by concurrent threads, such as in an increment
operation.
// protection: make shared data
synchronized & static
Deadlock
Intuitively we know
the feeling of deadlock when the
computer 'locks up' or we are stuck in a traffic jam,
especially the sort where vehicles are blocking each
other from moving at an intersection of two lanes.
Synchronization can lead to this special condition,
known
as 'deadlock'. The condition is
also called 'starvation' or
'deadly embrace'.
Deadlock
describes the situation where
neither of two threads can proceed because the
other holds
the lock that it needs.
Because neither can proceed, neither
can leave
the synchronized block they are in
and as a result,
neither can relinquish the lock
the other thread needs.
All deadlock
conditions are based on design errors.
Deadlock
can be avoided by following a
same sequence
for obtaining the locks and
avoiding
'mirror' symmetries
that result
in paradoxical deadlock scenarios.
Example of an Orchestrated Deadlock
Thread 1 | Process to Deadlock | Thread 2 |
run( ){ synchronized (ObjectA){ sleep(500); ObjectB.methodB( ); } } |
Thread 1 obtains
lock on
ObjectA and goes to
sleep
allowing
Thread
2
to
start
and obtain the
lock
on ObjectB and then
also go to sleep. Thread
1
regains
control
and
calls
the method on
Object B but B is
under
the lock of Thread 2. Thread 2 wakes and attempts
to call ObjectA's method
but
it
is
under
the
lock of Thread one
and is also
inaccessible.
Now the threads are in a deadlock or deadly embrace. |
run( ){ synchronized (ObjectB){ sleep(500); ObjectA.methodA( ); } } |
ObjectA
methodA( ) |
ObjectB methodB( ) |
The Internet
supplies
Java deadlock examples. The Java
programmers really has to make an effort to reliably create
the 'deadlock' condition. The examples are long so the links
are listed rather than the code. The 'deadly embrace' pattern
is more discernible in the second of the following examples.
Java Deadlock Examples
http://java.sun.com/docs/books/faq/src/thr/DeadlockExample.java
http://www.java2s.com/Code/Java/Threads/Deadlock.htm
The following
example is from the java2s site and is quite
'deadly' in that it is so short yet quick to lock. This code uses
inlined creations of Thread objects. The classic pattern is
in it as the first thread locks on synchronized resource 1
and defines resource 2. Symmetrically, Thread 2 locks
on
resource 2 and defines resource 1.
DeadLock Example //
from the java2 site
//
http://www.java2s.com/Code/Java/Threads/Anotherdeadlockdemo.htm
public class
AnotherDeadLock {
public static void main(String[] args) {
final Object resource1 = "resource1";
final Object resource2 = "resource2";
//
t1
tries
to
lock resource1 then resource2
Thread t1 = new Thread( ) {
public void run() {
// Lock
resource 1
synchronized (resource1) {
System.out.println("Thread 1: locked resource 1");
try {
Thread.sleep(50);
} catch
(InterruptedException e) {
}
synchronized
(resource2) {
System.out.println("Thread 1: locked resource 2");
}
}
}
};
// t2 tries to
lock resource2 then resource1
Thread t2 = new Thread() {
public void run( ) {
synchronized (resource2) {
System.out.println("Thread 2: locked resource 2");
try {
Thread.sleep(50);
} catch
(InterruptedException e) {
}
synchronized
(resource1) {
System.out.println("Thread 2: locked resource 1");
}
}
}
};
// If all goes as
planned, deadlock will occur,
//
and
the
program
will never exit.
t1.start();
t2.start();
}
}
Threads that
are Related,
Synchronized
and Cooperating
The method's of the object class wait(), notify(
) & notifyAll( )
can be used to allow
threads to communicate making their
operation
more efficient. This is useful in the
circumstance
where there is no point in a thread
running unless another
thread has
finished it's task.
Object
Class Thread Methods
wait( ) -
The wait( ) method is called by a thread object
to release itself from the
object
that
is the monitor and
return to the wait state.
The object has made itself
inactive and is waiting to be
notified of some condition
by another thread.
// makes itself wait
notify( )
- The notify( ) method is used by a thread to
notify( ) some other
thread
that
a particular condition
has become true and that
the other thread should come
out of the
wait state and once again seek control of the
monitor.
// notifies one
notifyAll( )
-
The notifyAll( ) method is like notify( ) except
that it notifies all
the
waiting thread that some condition is
true.
The three methods
of the Object class can be used to
create what is
referred to as
the 'Producer Consumer'
model where a producer generates some condition
such
as
information being available, notifies a waiting thread
that the
material
is available to
be consumed.
Producer Consumer Model in a Word
Example
A Post Office Analogy of the Producer
Consumer Model
// if you have the time.
Circumstances may exist where a thread seeks and gains the CPU to check a condition. The condition hasn't changed so CPU time was wasted in the process. Carry this thought into an analogy of a citizen checking the post office for the arrival of a parcel. But rather than having time wasted with unsuccessful checks, the postman suggests the customer wait and be notified when the parcel arrives. In this analogy, the post office is the program in which threads (people) are queued (synchronized) for service. (Only one person has the postman's attention at one time). When the postman (the CPU) checks for your parcel, he see's if it is in. (The act of 'checking' is the role of a synchronized method. Whether the parcel is in or not describes a boolean condition used to tell the thread to wait or to be notified, in the analogy, whether the person should come back to the post office.) The person waiting is analogous to a thread being blocked. The object being used to synchronize
'keeps' it's
threads in a 'waiting pool'. When the
condition changes
(i.e.the parcel
arrives and boolean is switched to true)
the
object can notify the waiting threads
to return to the ready state to contend for the
CPU. Note if The producing side of the equation can also participate in this analogy. Mail delivery vehicles arriving at the post office can participate in 'calling' (methods) from the post office to dispatch the notification that the wait for a parcel can be discontinued. Meanwhile other operations at the post office (representing static synchronized methods and non-synchronized methods) would carry on independently, business as usual. |
Producer Consumer Code Sample
One example where the producer consumer model
is useful
is the act of loading and
firing a cannon. If the loading of the
charge
and the cannon ball is not finished, there is
no point
in the one who fires the cannon to put
flame to the cannon's
wick. (This is an
old time cannon.)
Similarly, there is no point
in the loader trying to reload a
cannon if
it hasn't been fired. The wait( ) and notify(
) methods
are used to communicate this
information in the following code
example.
// Class Cannon has just two members, the load and fire methods.
class Cannon{
// the synchronized load method
synchronized boolean
load(boolean
loaded){
while(loaded == true){
try{
wait(
);
// wait( ) method from the Object class
}
catch(InterruptedException e){ }
}
loaded=true;
System.out.println
(Thread.currentThread().getName()
+ ": loaded ");
notify(
);
// notify( ) method from the Object class
return loaded;
}
// the synchronized fire
method
synchronized boolean fire(boolean loaded){
while(loaded
==
false){
try{
wait(
);
// wait( ) method from the Object class
}
catch( InterruptedException
e){ }
}
loaded=true;
System.out.println(Thread.currentThread().getName()
+": fired ");
loaded=false;
notify(
);
// notify( ) method from the Object class
return loaded;
}
}
/* The Gunners class extends Thread
and provides the necessary
run method( ). It also creates three
instances of itself. These new
threads will be invoking the run( )
method
once they are
started.
In
the run method an instance of the Cannon class is
created
to provide
the synchronized
load
and
fire
methods.
When methods are declared
synchronized
they
are implicitly
synchronized
on
this,
the
containing
class instance.
*/
class
Gunners
extends
Thread
{
Gunners(String s){
super(s);
}
static int i=0;
boolean loaded = false;
public static void main(String args[]){
Gunners
g1=new
Gunners("A.
Left"
);
Gunners g2=new
Gunners("B.
Right"
);
Gunners g3=new
Gunners("C.
Center");
g1.start(
);
g2.start( );
g3.start( );
}
public void run( ){
for(int
i=0;
i<3;i++){
Cannon m=new
Cannon( );
boolean b
=false;
b=m.load(b);
b=m.fire(b);
try{Thread.currentThread().sleep
// delays of war
(500);}catch(InterruptedException
ie){ }
}
}
}
Piped Streams
While wanting to
maintain the focus on Threads it should
be pointed
out that
the consumer producer model can be
emulated using classes from the
java.io
package. Piped
streams can communicate data to each
other directly
using PipedInput
and PipedOutputStream. They do this by
virtue or a shared buffer in memory. A PipedOutputStream
can be opened on
a PipedInputStream or visa versa.
Example
PipedInputStream in=new PipedInputStream( );
PipedOutputSteam out=new PipedOutputSteam( in);
//
notice
pipes
are
different.
An 'out' opens on an 'in'
Inside
the
Consumer
code
below
a PipedInputStream is
opened on the PipedOutputStream object that is created
in the Producer.
The following
example is from Peter Van der Linden's
text 'Just Java'. It should be noted that on a relatively
late version of Linux (Mandrake Linux 10.0) a lot more
bananas are being produced than consumed. The 'mod
20' was added to make the producer less zealous on
faster machines.
'Mod' ification
return
(
(System.currentTimeMillis()
-
start) % 20);
Sample showing the Consumer Producer
Pattern Using Pipes
//
from
Peter Van der Linden's text 'Just Java', Sun Press
import
java.io.*;
public class expipes{
public static void main(String args[]){
Producer p = new Producer( );
p.start();
Consumer c = new Consumer(p);
c.start( );
}
}
class Producer extends Thread{
protected PipedOutputStream po = new PipedOutputStream();
private DataOutputStream dos = new DataOutputStream(po);
public void run( ){
//
just
keep
producing
numbers that represent the
//
amount
of
millisecs
program has been running
for(;;) produce( );
}
private final long start =
System.currentTimeMillis( );
private final long banana(){
return (
(System.currentTimeMillis( ) - start) % 20);
}
void produce( ){
long t = banana();
System.out.println("produced " + t);
try{ dos.writeLong(t);}
catch (IOException ie){System.out.println(ie);}
}
}
// This
class consumes everything sent over the pipe.
// The
pipe does the synchronization. When the pipe
// is full, this
thread's read from the pipe is blocked
class Consumer extends Thread{
private PipedInputStream pip;
private DataInputStream d;
// java
constructor idiom, save argument
Consumer(Producer who){
try{
pip = new PipedInputStream(who.po);
d = new DataInputStream(pip);
} catch(IOException ie){
System.out.println(ie);
}
}
long get(){
long i=0;
try{ i=d.readLong( ); // read from pipe
} catch(IOException
ie){System.out.println(ie);}
return i;
}
public void run(){
java.util.Random r =
new java.util.Random();
for(;;){
long result = get();
System.out.println("consumed: " + result);
// next
lines are just to make things asynchronous
int randomtime = r.nextInt( ) % 1250;
try {sleep(randomtime);} catch(Exception e){}
}
}
}
Multithreading is used frequently in server programs.
By
providing a separate
thread process to each client a server
is able
to handle several clients at the
same time. The
simplest approach is to spawn
a new thread to service
each
client that presents itself to the server. This
may
create a problem if too many
threads are spawn and
system resources begin
to be seriously drained.
One
approach to correct for this is to create a
program
that
limits the number of
threads that are spawned and have
them provide
client services. Another
popular approach
is to use a mechanism called 'thread
pooling' where thread
objects are
maintained in a thread pool and can be issued
to different object references as required. To
characterize
a multithreaded server we will
first create a skeleton for
the
code.
Example of A Simple Multithreaded Server Skeleton
The Simple Multithreaded server creates a Thread
subclass
whose run( )
method does all the server stuff. In this version
the constructor is designed
to use the socket object that is
generated by
the ServerSocket's accept( )
method.
Sample of a
Server Skeleton Creating a Thread per Service Call
Multithreaded Server with Limited Service Threads
Too many threads may represent a form of
'memory leak'
as thread resources
may not be considered entirely collectible
by the
garbage collector. The next
example shows a server
that creates a limited number of threads.
This example of a
server with
limited service threads, is
taken from Heller & Robert's 'Java Developer's Handbook,
published by Sybex. The key feature in this to notice is the
Thread
constructor
used
for each of the threads takes the
Server itself as the Runnable object,
followed
by names
we are giving to each thread.
Sample of a Server Utilizing a Limited
Number of Threads
// MultiServe.java
exampled in the 'Java Developer's
// Handbook'' by Heller &
Roberts published by Sybex
import java.net.*;
import java.io.*;
import java.util.*;
public class MultiServe implements Runnable{
private ServerSocket ss;
public static void main(String args[]) throws Exception{
MultiServe m = new MultiServe();
m.go();
}
public void go() throws Exception{
// socket
set to an arbitrary port ed.
ss = new ServerSocket(1313, 5);
Thread t1 = new Thread(this, "1");
Thread t2 = new Thread(this, "2");
Thread t3 = new Thread(this, "3");
t1.start( );
t2.start( );
t3.start( );
}
public void run(){
Socket s= null;
BufferedWriter out = null;
String myname =
Thread.currentThread().getName();
for(;;){
try{
System.out.println("thread " +
myname +
"
about to accept..");
s= ss.accept( );
System.out.println("tread " + myname +
" accepted a connection");
out = new BufferedWriter(
new
OutputStreamWriter(s.getOutputStream()));
out.write(myname + " " + new Date());
Thread.sleep(10000);
out.write("\n");
out.close();
}
catch(Exception e){
e.printStackTrace();
}
}
}
}
This is a nice model. Where the first approach
used one
loop that was serviced
by any number of service threads,
in this case
the server functions are provided
entirely in
parallel by three separate threads
each with their own
listening loop.
Notice to use the
above
code you will have need to add
functionality and write your own clients.
A Brief Note of Thread Pools
Many applications
are transaction-oriented where perhaps
1000s of very
short
sessions are required. The overhead of
making client server connections
in this
scenario is very high.
An alternative is to use a Thread Pool. Thread
pools are
commonly used in database management systems, CORBA
and Enterprise
JavaBeans.
Cliff Berg outlines
a Thread Pool based on the Consumer
Producer
Model. In this scenario the Producer is the main
server object. A fixed
pool of
thread objects are created and
put into the wait state. On each client
request,
a service object
is created, put into a queue and notify( ) is called.
One of the
threads in the pool will resume and attempt to obtain the
service
object
from
the queue. If it succeeds it processes
the object. If it fails it goes
back to the
wait state with the
other threads in the pool.
//
pool of fish waiting for the bait
Using Threads
In Animations
The run method can
be used in conjunction with a call the
Thread's sleep method and a loop that increments a counter
to control an animation. In the following code a line is drawn
in successive stages under the control of the run( ) method.
Because the Color constructor's argument's finally goes out
of range at a high value of the variable i, we have used the
exception thrown to throw a flag to terminate the drawing loop.
Animation Example Using Run as a Frequency
Controller
import javax.swing.*;
import
java.awt.*;
public class Animation {
JFrame jf;
PaintPanel pp;
Animation(){
jf = new JFrame();
pp= new PaintPanel();
jf.getContentPane().add(pp);
jf.setSize(600,600);
jf.setVisible(true);
Thread t=new Thread(pp);
t.start();
}
public static void main(String[] args){
new Animation();
}
}
class PaintPanel extends JPanel implements Runnable{
int i=0;
boolean flag=true;
public void paint(Graphics g){
try{
g.setColor(new
Color(i,i+i,255-i));
g.drawLine(260-(i*2),220+(i*2),330+(i*2),280-(i*2));
}
catch(IllegalArgumentException
iae){
System.out.println
("Out of
number range for the Color Constructor");
flag=false;
//
System.exit(0); // could use
System.exit( 0 ) alone
}
}
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
public void run(){
try{
while (flag){
Thread.sleep(100);
i++;
repaint();
}
}
catch(InterruptedException ie){}
}
}
1) Which of the
following statements is not correct?
a) Threads allow
several concurrent activities to occur by spawning
new processes.
b) A system thread is a schedulable sub-process.
c) Each operating system's thread implementation is different.
d) Java supplies a generic thread model that is portable across
operating
systems
2) Consider two
threads. One is generating a number that will be used
by the
second thread. The first thread will block
until the second thread which is
generating the number notifies it that the
number is ready. Which of the
following best describes the thread
interactions.
a) Unrelated Threads
b) Related but Unsynchronized
c) Related and Synchronized
d) Related, Synchronized and Cooperating
3) All
the following terms were used to describe the synchronization process.
Which one is an abbreviation for two other words?
a) monitor
b) semaphore
c) mutex
d) lock
4) [True or False] Concurrent reentrant
code maintains concurrency by
not using variables that are defined locally.
5) Which of the following statements is not correct?
a) The synchronized keyword can be applied to
a block of code
b) a synchronized section cannot be put inside
a non-synchronized method
c) In a synchronized block the object that
supplies
the monitor is placed
in round brackets between the synchronized
keyword
and the block that
is being synchronized.
d) The synchronized keyword effectively makes
a section of code atomic.
6) Which of the
following is not an Object class method used by synchronized
threads to communicate.
a) wait( )
b) waitAll( )
c) notify( )
d) notifyAll( )
Exercise
1) Finish the
'Multithreaded
Server with Limited Service Threads'
to supply a time in a distant country to a client. You need to supply
the client as well.