Short Course Manual Template

foreheadsobstinacyΛογισμικό & κατασκευή λογ/κού

15 Αυγ 2012 (πριν από 4 χρόνια και 10 μήνες)

308 εμφανίσεις



University of Ulster

Faculty of
Engineering


Page
1

Java
-

Semaphores

Java Session 0
4



Semaphores


Introduction

A semaphore is a
programming construct which allows processes to synchronise
their activities in various ways. A semaphore is defined and used as follows
. It is
a global variable

s

initialised with an

internal

integer value defined by the
programmer.
There are two principal operations defined on the semaphore,
P(
s
)

and
V(
s
)
. To execute
P(
s
)

a process waits until the value of
s

is greater than 0 and
decre
ments
s

by 1.
To execute
V(
s
)
a process increments
s

by 1.
Both
P()

and
V()
are implemented as atomic actions.


Java Implementation of Semaphores

The Java programming language contains a package of classes which can be used
to help programs to synchronise.

To access these classes you
must import the
package by adding the line:

import java.util.concurrent.*;


to your class definitions.


The
Java
Semaphore class has methods which correspond to
P()
and
V()
as
follows:


Textbook

Java

sem

s

= init;

Semaphor
e s = new Semaphore(init);

P(s);

s.acquire();

V(s)

s.
release();


T
o a use semaphore in
a
Java
program

that has threads, you must


STEP
1.
Create a Semaphore object in your
main()

method,

STEP
2. Pass a reference to

(address of)
this

Semaphore

objec
t to the constructor
method of each of the Threads in your program.

STEP
3.
Keep

an internal variable in each Thread
.

This variable

stores the
address
of the
semaphore
.

STEP
4. Use the internal variable in each Thread's
run()
method.

Critical Section
Pro
blem

The Critical Section problem is easy to solve with a semaphore

in Java
.

1.
Firstly you create a shared

Semaphore

object

s

in the main program
, with initial
value set to 1
.

2. Next

you create two (or possibly more) thread objects and pass
s

as a par
ameter


University of Ulster

Faculty of
Engineering


Page
2

Java
-

Semaphores

to the constructor of each of them.



3. Yo
u
edit each Thread class as follow
s. You create an internal

variable

of type
Semaphore

in each Thread
class. In the constructor of the Thread object you
assign the internal variable to the
parameter

value.


4. In the
run()
method of the Thread class, you use



s.acquire()

to enter the Critical Section. This is the same as
P(s)

in the lecture notes.

5. When a Thread object exits the Critical Section it must execute:


s.release()

which is the same as
V(s)

in the lecture notes.


Getting Started

Download the zip file

prac0
4
.zip and unzip it

to your
program directory.

Your program directory should now contain the following folder
s
:

criticalsemaphores

mailbox

carpark

holbysemaphores

kanga4


Start Netbeans

Op
en the project called
criticalsemaphores
.




University of Ulster

Faculty of
Engineering


Page
3

Java
-

Semaphores

Critical Section in Java

You will see three Java class files in this project: Main.java, P1.java and P2.java.

Look at Main.java
. You can see STEP 1 which creates the global object
s
, which
is of class Semaphore.

In STEP 2 you pass the address of
s

to the constructor of
P1 and
the constructor of P2.

Now look at P1.java. In STEP 3, the constructor for P1
assigns the global
address

of the
semaphore to the internal variable s:





this.s = semIn;


Finally, i
n the run method of P1 we see the use of the semaphore in STEP 4,
where
s.acquire()

is the entry
protocol

and

s.release()

is the exit protocol.

NOTE that
s.acquire()

must be in the
try()

section of a
try() … and catch()

block
so that the program must
catch

InterruptedException.


/*


* Main.java


*/


package criticalsemaphores;


import java.util.concurrent.*; // Concurrency package is available


public class Main {




public static void main(String[] args) {



// STEP 1: create semaphore with va
lue 1


Semaphore s = new Semaphore(1);




P1 p1 = new P1(s); // STEP 2: Create threads,


P2 p2 = new P2(s); // call threads' constructor methods



p1.start(); // calls run() method in Thread


p2.start();




System.out.println("Threads p1 and p2 commanded to start");


}

}



University of Ulster

Faculty of
Engineering


Page
4

Java
-

Semaphores

/*


* P1.java


*/

package criticalsemaphores;


import java.util.concurrent.*;


public class P1 extends Thread {




private Semaphore s; // STEP 3: internal variable stores


// address of global semaphore object



// STEP 3: Thread's constructor method, gets reference to


// global variable from main() method.


public P1(Semaphore semIn) {


this.s = semIn;


}


public void run() {


int round;


for (round = 1; round < 4; round++) {


System.out.println(


"P1 trying to enter CS, round:" + round);




// STEP 4: Critical Section Entry protocol



// Same as P(sem);


try {


s.acquire();


}


catch ( InterruptedException e) {}




System.out.println(


"P1 is in Critical Section
, round:" + round);


try {


sleep(5000);


} catch (InterruptedException e) {}



//STEP 4: Critical Section exit protocol


// Same as V(sem)


System.out.println(



"P1 leaving Critical Section, round:" + round);




s.release();


try {


Thread.sleep(500);


} catch (InterruptedException e) {}


}


System.o
ut.println("P1 finished");


}

}


Exercise

1. Run the project criticalsemaphores.


You should see the two processes using the Critical Section.

2. Does the program appear to be fair?

3. Define a third class P3 which does the same as P1 and P2. (See if you can write
the program for P3 without having to cut and paste from P1 and P2. This

should

help you to understand what is happening…)



University of Ulster

Faculty of
Engineering


Page
5

Java
-

Semaphores

The
Mailbox

Problem

This is the same example as in the lectures. A producer process creates a message,
puts it into a shared mailbox. A consumer process reads the message from the
mailbox.

The concurre
nt program requires the following synchronisation.

1. The producer must wait for the mailbox to be empty before it puts the message
into to.

2. The consumer program must wait for the mailbox to be full before it is allowed
to read the message.

These two sy
nchronisation conditions are implemented with semaphores,
empty

and
ful
l.

Initially
empty

has the value 1, and
full

has the value 0.

The producer waits on
empty
, and signals
full
.

The consumer waits on
full

and signals
empty
.


Mailbox
-

Java Implementa
tion

The implementation below is in the Netbeans project called

mailbox
.

The Main class creates the shared objects,
x
,
empty

and
full
and two threads.

Note that the message is kept in the shared data object called
AnyData
. Initially
AnyData
contains the
junk value 5.7: this will be overwritten by the first message
produced by the producer.

We have a producer process implemented

as an object
producer1

using

Java class,
Producer, and a consumer process

consumer1
, implemented in the Java class
Consumer.


/*


* Main.java


*/

package mailbox;


import java.util.concurrent.*; // Concurrency package


public class Main {


public static void main(String[] args) {



AnyData x = new AnyData(5.7);


Semaphore empty = new Semaphore (1);


Semap
hore full = new Semaphore (0);



Producer producer1 = new Producer(empty, full, x);


Consumer consumer1 = new Consumer(empty, full, x);



producer1.start();


consumer1.start();


System.out.println("Threads have been comm
anded to start");


}


}




University of Ulster

Faculty of
Engineering


Page
6

Java
-

Semaphores

/*


* AnyData.java


*/

package mailbox;


public class AnyData {


public double message;


public AnyData(double startValue) {


message = startValue;


}

}


/*


* Producer.java


*/

package mailbox;


impo
rt java.util.concurrent.*; // Concurrency package


public class Producer extends Thread {


private Semaphore empty;


private Semaphore full;


private AnyData pillarbox;




public Producer(Semaphore empty, Semaphore full, AnyData pbo
x) {


this.empty = empty;


this.full = full;


this.pillarbox = pbox;


}


public void run() {


int round;



for (round = 0; round < 3; round++)


{ /* Trying to send a message */



double mess = Math.random();


System.out.println("Producer waits empty box, round:" +


round +


" message:" + mess + " P(empty)");


try

{


empty.acquire(); // Wait for box to be empty


}


catch (InterruptedException e) {}




/* Copying message */


Delay.skip(2.0);


pillarbox.message

= mess;


System.out.println("Producer has put, round:"


+ round + " V(full)");


full.release(); // Signal that box is full


Delay.skip(5.0);


}


Sys
tem.out.println("Producer finished");


}


}




University of Ulster

Faculty of
Engineering


Page
7

Java
-

Semaphores

/*


* Consumer.java


*/

package mailbox;


import java.util.concurrent.*; // Concurrency package


class Consumer extends Thread {


priv
ate Semaphore empty;


private Semaphore full;


private AnyData pillarbox;




public Consumer(Semaphore empty, Semaphore full, AnyData pbox) {


this.empty = empty;


this.full = full;


pillarbox = pbox;



}


public void run() {


int round;


double x;




for (round = 0; round < 3; round++) {


System.out.println(" Consumer waits full box, round:"


+ round + " P(full)");


t
ry {


full.acquire();


}


catch (InterruptedException e) {}




x = pillarbox.message;


Delay.skip(1.0);


System.out.println(" Consumer finished read round:" +



round +


" message:" + x + " V(empty)");


empty.release();


Delay.skip(2.0);


}


System.out.println(" Consumer finished");


}


}



Exercise

1. Load the mailbox program into Netbeans.

2
. Run the mailbox program.

3
.
Count t
he number of
Ps

and
Vs

for each of the semaphores. Do they match?

4
.
The producer is producing messages which are random numbers between 0 and
1. Is the consumer reading all of the messages and are the messages being read
correctly?

5
. Comment out the c
alls to
release()

and
acquire()

and see what happens. Explain
why it happens.

7. If you have time, draw a timeline showing
what

goes on. (Draw a timeline for
each process, marking events along the time line, using a ruler to measure the
times between event
s.

Bounded Buffer

-

Holby Hospital

The
Bounded Buffer semaphore example is implemented here, using the


University of Ulster

Faculty of
Engineering


Page
8

Java
-

Semaphores

Ambulance Porter and Ward Porter example from the first lecture.

The buffer is a set of four beds. The Ambulance Porter (AP) puts patients into the
buffe
r and the Ward Porter (WP) removes patients from the buffer. There are two
semaphores, empty and full, which control access to the buffer.

The value of empty is initially four, corresponding to four empty beds, and full is
initially zero, corresponding to
no full beds.

As the ambulance porter works away the beds fill up and the Ward porter empties
the beds.

Note that there are three shared objects in the program,
buffer, empty and full and
these are used as parameters passed to the constructors of Ambulan
cePorter and
WardPorter.


/*


* Main.java


*/


package holbysemaphore;


import java.util.concurrent.*;


public class Main {


public static void main(String[] args) {


Buffer buffer = new Buffer(4);


Semaphore empty = new Semaphore (4);


Se
maphore full = new Semaphore (0);


AmbulancePorter ap = new AmbulancePorter(empty, full, buffer);


WardPorter wp = new WardPorter(empty, full, buffer);


ap.start();


wp.start();


}

}


/*


* Buffer.java


*/

package holbysemaphore;


pu
blic class Buffer {


int length;


int front = 0;


int rear = 0;


String[] beds = null;




public Buffer(int length) {


this.length = length;


beds = new String[length];


}

}



/*


* AmbulancePorter.java


*/



University of Ulster

Faculty of
Engineering


Page
9

Java
-

Semaphores



package holb
ysemaphore;


import java.util.concurrent.*;


public class AmbulancePorter extends Thread {




private Semaphore empty;


private Semaphore full;


private Buffer buffer;




AmbulancePorter(Semaphore empty, Semaphore full,



Buffer buffer) {


this.empty = empty;


this.full = full;


this.buffer = buffer;


}


public void run() {



String[] patient = {"Mary", "Caroline", "Art", "Siobhan",


"Seosamh", "William",
"Sally", "Chen" };


int numPatients = patient.length;




for (int n = 0; n < numPatients; n++) {


Delay.skip(2.0);


System.out.println(


"Ambulance porter, wait for empty bed...");


try {



empty.acquire();


}


catch (InterruptedException e) {}




buffer.beds[buffer.front] = patient[n];


System.out.println("Ambulance porter at bed number:" +




buffer.fr
ont +


" Patient:" + n + " " + patient[n]);


buffer.front = (buffer.front + 1) % buffer.length;




full.release(); // V(full)


}


System.out.println("Ambulance Porter finished");



}

}




University of Ulster

Faculty of
Engineering


Page
10

Java
-

Semaphores

/*


* WardPorter.java


*/


package holbysemaphore;


import java.util.concurrent.*;


public class WardPorter extends Thread {


Semaphore empty;


Semaphore full;


Buffer buffer;




public WardPorter(


Semaphore empty,

Semaphore full, Buffer beds) {


this.empty = empty;


this.full = full;


this.buffer = beds;


}


public void run() {


int numPatients = 8;


String newPatient;




for (int n = 0; n < numPatients; n++) {



Delay.skip(10.);


try {


full.acquire();


}


catch (InterruptedException e) {}




newPatient = buffer.beds[buffer.rear];


System.out.println("Ward porter at bed:" +



buffer.rear +


" patient:" + n + " " +


newPatient);


buffer.rear = (buffer.rear + 1) % 4;



empty.release();


}


System.out.println("Ward porter finished");


}

}



Exercise

1. Run the program and observe wh
ich porter is working faster.

2. Alter some calls to

Delay.skip
()

to get the other porter to work faster and see
what happens.

3. In general, what happens to this type of program if you make the buffer larger?



University of Ulster

Faculty of
Engineering


Page
11

Java
-

Semaphores

The Car Park

This is yet another cla
ssic problem for semaphores. We use a semaphore
numSpaces

to represent the number of free spaces in a car park.

A car entering the car park executes
P(
numSpaces
)
and a car leaving the car park
executes

V(
numSpaces
).

In this Java program we use a Semaphor
e object
numSpaces
, initially set to 2,
corresponding to 2 free spaces.
The code
numSpaces.acquire()

corresponds to
P(
numSpaces
) and
numSpaces.release()

corresponds to V(
numSpaces
).

Note that a process can examine the internal value of a semaphore using
t
he
expression:

numSpaces.availablePermits()

Run the
carpark

project
.



/*


* Main.java


*/


package carpark;



import java.util.concurrent.*;


public class Main {




public static void main(String[] args) {


Semaphore numSpaces = new Sema
phore(2, true);




Car c1 = new Car("volvo", numSpaces);


Car c2 = new Car("datsun", numSpaces);


Car c3 = new Car("polo", numSpaces);


Car c4 = new Car("clio", numSpaces);


Car c5 = new Car("lotus", numSpace
s);




c1.start();


c2.start();


c3.start();


c4.start();


c5.start();


}

}




University of Ulster

Faculty of
Engineering


Page
12

Java
-

Semaphores

/*


* Car.java


*/


package carpark;


import java.util.concurrent.*;


public cla
ss Car extends Thread {


private String name;


private Semaphore numSpaces;




public Car(String name, Semaphore s) {


this.name = name;


numSpaces = s;


}


public void run() {


Delay.idleUpTo(2.0);



System.out.println(name + " about to try entering");


try {


numSpaces.acquire(); // equivalent to P(numSpaces)


}


catch ( Exception e) {}



System.out.println(name + " in car park. " +



"Number of spaces in park:" +


numSpaces.availablePermits());


Delay.idleUpTo(6.0);



System.out.println(" " + name + " about to leave, " +


numSpaces.availablePermits() + " spaces in park")
;




numSpaces.release(); // equivalent to V(numSpaces)



System.out.println(" " + name + " has left, " +


numSpaces.availablePermits() + " spaces in park");


}

}



University of Ulster

Faculty of
Engineering


Page
13

Java
-

Semaphores

Exercise
-

Project Planning Simulation

(medium hard)

Assume that

a series of tasks must be carried out in order to complete a project.

Task T1 must be done first. When T1 is complete, T2 and T3 may start.

Task T4
can start when T3
finishes. T5 can start when T
4 has finished and when T
2 has
finished.

Represent each ta
sk by a process.

The process
waits

until it has been told that it
can start, runs, then tells any processes that need to know that it has finished.

Use semaphores. When a task

starts it uses P(
sem1
) to wait
for its predecessors,
and when it

finishes it u
sed

V
(
sem2
)
to signal to its successors
, where sem1 and
sem2 are variables defining semaphores
.

1. Write down a set of programs

in pseudo
-
code
, one program for each task,
defining some semaphores as shared variables

in your program.

2.
Assume

the followin
g times for the length of each task

T1: 2 sec

T2: 3 sec

T3: 4 sec

T4:
2 sec

T5: 6 sec

3. Write a Java program in which each task is a thread
.

Simulate the length of each task with a time delay of the length shown above.

Use semaphores shared as shown

Time
the resulting program (Actually Netbeans shows the run
-
time.)

Is it what you expect?


Exercise
-

Kang
aroo

Race

(medium hard)

Write a program that uses semaphores
in

the
simulation

of a
kangaroo

race.

Create one thread for each
kangaroo

and one
thread

for a racecour
se Official.

The Official signals the race to start.

Each
kanga
roo

waits for
the starting signal from the Official.

Th
e
kanga
roo

runs
, taking some random length of time.
The Official

waits until
all
kangaroo
s have completed the race and then prints out the shortest time

that a
kanga
roo

has taken
.


Ex
ercise

-

Washing Up (easy)

A family has s
hared out the task of washing, drying and putting away

N
=30 plates.
Mary washes the plates and p
uts them onto a draining rack
. Art
collects each
plate from the drainer

an
d then dries it and puts it away. The dra
ining rack

where
Mary puts the

plates holds at most four plates.