Step 3
of 3: Sharing and Synchronization
Threads running concurrently can share resources like variables
and data, and can manipulate the file etc. So due to this, Synchronization
among threads becomes a key issue. The best way to illustrate
the Synchronization and Locking is through a simple code. Let us
consider a day to day example of Airline ticketing, where the
issuer allocates the ticket in a ticketpool, and the travel agents
collect the ticket from this ticketpool. Synchronization between
the actual allocation of tickets and collection is very much needed. The
source code for issuer.java is as follows:
import java.awt.*;
class issuer extends Thread
{
private Ticketpool
ticketpool;
private int ticket_no;
private int i;
public issuer( Ticketpool tp, int n)
{
ticketpool = tp;
this.ticket_no = n;
}
public void run()
{
for( i=0; i< 5 ; i++)
{
ticketpool.allocate(i);
System.out.println("Issuer has issued " + i );
try{
sleep((int)(Math.random() * 100));
}
catch(Exception e)
{
System.out.println(e.getMessage());
}
}
}
} |
The code starts with creating a object of type Ticketpool, call it
ticketpool, which is
the common object shared between the issuer and the travel agent.
The run() method has a for loop which allocates the ticket to the
ticketpool object via the method allocate(). Sleep() function is
used so that the thread gets temporarily in a non-runnable mode
which allows other thread to execute. Now consider a class
Travelagent, which collects the ticket from the ticketpool,
whose source code is as follows.
import
java.awt.*;
class travelagent extends Thread
{
private Ticketpool ticketpool;
private int travelagent_no;
private int i, ticket_no;
public travelagent( Ticketpool tp, int n)
{
ticketpool = tp;
this.travelagent_no = n;
}
public void run()
{
int
value = 0;
for(
int i = 0; i < 5 ; i++)
{
value = ticketpool.collect();
System.out.println("Travel Agent " +
this.travelagent_no + "collects" + value );
}
}
} |
Again the program makes an object of type Ticketpool and it
overrides the run() method which does the job of collecting the
tickets from the ticketpool through a method collect().
Now let us concentrate on the Ticketpool code as follows:
import java.awt.*;
class Ticketpool extends Thread
{
private boolean ticket_position = false;
private int position;
public synchronized int collect()
{
while ( ticket_position ==false )
{
try{
wait();
}
catch( Exception e)
{
System.out.println(e.getMessage());
}
}
ticket_position = false;
notifyAll();
return position;
}
public synchronized void allocate(int tno)
{
while ( ticket_position == true)
{
try{
wait();
}
catch( Exception e)
{
System.out.println(e.getMessage());
}
}
position = tno;
ticket_position = true;
notifyAll();
}
} |
In Java, the code segment that is used by different threads are
called Critical Sections. In Java the critical
sections have to be grouped into different methods, and are
differentiated from the normal methods with the keyword synchronized.
Java allocates a single lock with the object that has
the synchronized keyword. The Ticketpool class has two methods
that are synchronized. Hence each of the issuer and the travel
agent who instances the Ticketpool object has a unique
lock. Hence when the issuer issues the ticket by allocate()
method of the Ticketpool, the lock remains with it and hence the
travel agent cannot access this object. Similarly when the
Travelagent accesses the collect() method the lock remains with
it, and the issuer threads cannot access it. Note that the
handing over of the lock (acquiring the monitors for the
threads), and it's release is totally controlled by the runtime
system. Also note that in Java the monitors are reentrant, i.e.
a method can call another synchronized method
and so can re-acquire the monitor.
The collect() method waits till ticket_position is true which
occurs when the issuer issues the ticket through the
allocate() method. The wait is used for
temporary release of the control from the thread, sleep could
have also been used.The notifyAll method wakes up all threads
waiting on the object in question (in this case, the CubbyHole).
The awakened threads compete for the lock. One thread gets it,
and the others go back to waiting. Finally, source code for
the ticket.java is included below.
import
java.awt.*;
public class ticket
{
public static void
main(String[] argv)
{
Ticketpool
tpl;
travelagent
ta;
issuer
i;
tpl = new
Ticketpool();
ta = new travelagent(tpl,1);
i = new
issuer(tpl, 1);
i.start();
ta.start();
}
} |
The code mentioned above just makes an object of all the
classes discussed so far and then calls the start() method of
the two Thread's, which as discussed earlier calls run()
method along with other housekeeping functions.
Program result
The result of the program are shown below.
Issuer has
issued 0
Travel Agent 1collects0
Issuer has issued 1
Travel Agent 1collects1
Issuer has issued 2
Travel Agent 1collects2
Issuer has issued 3
Travel Agent 1collects3
Issuer has issued 4
Travel Agent 1collects4 |
This was a basic example illustrating various fundamentals
of Java Multithreading. Using these, various interesting
applications can be built.
|