Book Chapter 5
Concurrency: monitors & condition synchronization 1
©Magee/Kramer 2nd Edition
Chapter 5
Monitors &
Condition Synchronization
Concurrency: monitors & condition synchronization 2
©Magee/Kramer 2nd Edition
monitors & condition synchronization
Concepts: monitors:
encapsulated data + access procedures
mutual exclusion + condition synchronization
single access procedure active in the monitor
nested monitors
Models: guarded actions
Practice: private data and synchronized methods (exclusion).
wait(), notify() and notifyAll() for condition synch.
single thread active in the monitor at a time
Concurrency: monitors & condition synchronization 3
©Magee/Kramer 2nd Edition
5.1 Condition synchronization
A controller is required for a carpark, which only permits
cars to enter when the carpark is not full and does not
permit cars to leave when there are no cars in the carpark.
Car arrival and departure are simulated by separate threads.
Concurrency: monitors & condition synchronization 4
©Magee/Kramer 2nd Edition
carpark model
♦ Events or actions of interest?
arrive and depart
♦ Identify processes.
arrivals, departures and carpark control
♦ Define each process and interactions (structure).
ARRIVALS CARPARK
CONTROL
DEPARTURESarrive depart
CARPARK
Concurrency: monitors & condition synchronization 5
©Magee/Kramer 2nd Edition
carpark model
CARPARKCONTROL(N=4) = SPACES[N],
SPACES[i:0..N] = (when(i>0) arrive->SPACES[i-1]
|when(i
).
ARRIVALS = (arrive->ARRIVALS).
DEPARTURES = (depart->DEPARTURES).
||CARPARK =
(ARRIVALS||CARPARKCONTROL(4)||DEPARTURES).
Guarded actions are used to control arrive and depart.
LTS?
Concurrency: monitors & condition synchronization 6
©Magee/Kramer 2nd Edition
carpark program
♦ Model – all entities are processes interacting by actions
♦ Program – need to identify threads and monitors
♦thread – active entity which initiates (output) actions
♦monitor – passive entity which responds to (input) actions.
For the carpark?
ARRIVALS CARPARK
CONTROL
DEPARTURESarrive depart
CARPARK
Concurrency: monitors & condition synchronization 7
©Magee/Kramer 2nd Edition
carpark program – class diagram
Applet
Runnable
ThreadPanel
CarParkControl
Arrivals
Departures
DisplayCarParkCarParkCanvas
CarPark
arrivals,
departures
arrive()
depart()
carDisplay
carpark
disp
We have omitted
DisplayThread
and
GraphicCanvas
threads managed by
ThreadPanel.
Concurrency: monitors & condition synchronization 8
©Magee/Kramer 2nd Edition
carpark program
Arrivals and Departures implement Runnable,
CarParkControl provides the control (condition synchronization).
Instances of these are created by the start() method of the
CarPark applet :
public void start() {
CarParkControl c =
new DisplayCarPark(carDisplay,Places);
arrivals.start(new Arrivals(c));
departures.start(new Departures(c));
}
Concurrency: monitors & condition synchronization 9
©Magee/Kramer 2nd Edition
carpark program – Arrivals and Departures threads
class Arrivals implements Runnable {
CarParkControl carpark;
Arrivals(CarParkControl c) {carpark = c;}
public void run() {
try {
while(true) {
ThreadPanel.rotate(330);
carpark.arrive();
ThreadPanel.rotate(30);
}
} catch (InterruptedException e){}
}
}
Similarly Departures
which calls
carpark.depart().
How do we implement the control of CarParkControl?
Concurrency: monitors & condition synchronization 10
©Magee/Kramer 2nd Edition
Carpark program – CarParkControl monitor
class CarParkControl {
protected int spaces;
protected int capacity;
CarParkControl(int n)
{capacity = spaces = n;}
synchronized void arrive() {
… –spaces; …
}
synchronized void depart() {
… ++spaces; …
}
}
condition
synchronization?
block if full?
(spaces==0)
block if empty?
(spaces==N)
mutual exclusion
by synch methods
Concurrency: monitors & condition synchronization 11
©Magee/Kramer 2nd Edition
condition synchronization in Java
Java provides a thread wait set per monitor (actually per object)
with the following methods:
public final void notify()
Wakes up a single thread that is waiting on this object’s wait set.
public final void notifyAll()
Wakes up all threads that are waiting on this object’s wait set.
public final void wait()
throws InterruptedException
Waits to be notified by another thread. The waiting thread
releases the synchronization lock associated with the monitor.
When notified, the thread must wait to reacquire the monitor
before resuming execution.
Concurrency: monitors & condition synchronization 12
©Magee/Kramer 2nd Edition
condition synchronization in Java
We refer to a thread entering a monitor when it acquires the mutual
exclusion lock associated with the monitor and exiting the monitor
when it releases the lock.
Wait() – causes the thread to exit the monitor,
permitting other threads to enter the monitor.
Thread A Thread B
wait()
notify()
Monitor
data
Concurrency: monitors & condition synchronization 13
©Magee/Kramer 2nd Edition
monitor lock
wait()
Monitor
data
wait
Thread C
Thread E
Thread B
Thread F
Thread A
notify()
Thread B
Thread F
Thread E
Thread A
Thread C
Thread A
Concurrency: monitors & condition synchronization 14
©Magee/Kramer 2nd Edition
condition synchronization in Java
FSP: when cond act -> NEWSTAT
Java: public synchronized void act()
throws InterruptedException
{
while (!cond) wait();
// modify monitor data
notifyAll()
}
The while loop is necessary to retest the condition cond to ensure that
cond is indeed satisfied when it re-enters the monitor.
notifyall() is necessary to awaken other thread(s) that may be
waiting to enter the monitor now that the monitor data has been changed.
Concurrency: monitors & condition synchronization 15
©Magee/Kramer 2nd Edition
CarParkControl – condition synchronization
class CarParkControl {
protected int spaces;
protected int capacity;
CarParkControl(int n)
{capacity = spaces = n;}
synchronized void arrive() throws InterruptedException {
while (spaces==0) wait();
–spaces;
notifyAll();
}
synchronized void depart() throws InterruptedException {
while (spaces==capacity) wait();
++spaces;
notifyAll();
}
}
Is it safe to use notify()
here rather than notifyAll()?
Concurrency: monitors & condition synchronization 16
©Magee/Kramer 2nd Edition
models to monitors – summary
Active entities (that initiate actions) are implemented as threads.
Passive entities (that respond to actions) are implemented as monitors.
Each guarded action in the model of a monitor is
implemented as a synchronized method
which uses a while loop and wait() to
implement the guard. The while loop condition is
the negation of the model guard condition.
Changes in the state of the monitor are signaled to
waiting threads using notify() or notifyAll().
Concurrency: monitors & condition synchronization 17
©Magee/Kramer 2nd Edition
5.2 Semaphores
Semaphores are widely used for dealing with inter-process
synchronization in operating systems. Semaphore s is an
integer variable that can take only non-negative values.
down(s): if s >0 then
decrement s
else
block execution of the calling process
up(s): if processes blocked on s then
awaken one of them
else
increment s
The only
operations
permitted on
s are up(s)
and down(s).
Blocked
processes are
held in a
FIFO queue.
Concurrency: monitors & condition synchronization 18
©Magee/Kramer 2nd Edition
modeling semaphores
To ensure analyzability, we only model semaphores that
take a finite range of values. If this range is exceeded
then we regard this as an ERROR. N is the initial value.
const Max = 3
range Int = 0..Max
SEMAPHORE(N=0) = SEMA[N],
SEMA[v:Int] = (up->SEMA[v+1]
|when(v>0) down->SEMA[v-1]
),
SEMA[Max+1] = ERROR.
LTS?
Concurrency: monitors & condition synchronization 19
©Magee/Kramer 2nd Edition
modeling semaphores
Action down is only accepted when value v of the
semaphore is greater than 0.
Action up is not guarded.
Trace to a violation:
up up up up
up up
down
up
down
up
down
-1 0 1 2 3
Concurrency: monitors & condition synchronization 20
©Magee/Kramer 2nd Edition
semaphore demo – model
Three processes p[1..3] use a shared semaphore mutex
to ensure mutually exclusive access (action critical) to
some resource.
LOOP = (mutex.down->critical->mutex.up->LOOP).
||SEMADEMO = (p[1..3]:LOOP
||{p[1..3]}::mutex:SEMAPHORE(1)).
For mutual exclusion, the semaphore initial value is 1. Why?
Is the ERROR state reachable for SEMADEMO?
Is a binary semaphore sufficient (i.e. Max=1) ?
LTS?
Concurrency: monitors & condition synchronization 21
©Magee/Kramer 2nd Edition
semaphore demo – model
p.1.mutex.down
p.2.mutex.down
p.3.mutex.down p.3.critical
p.3.mutex.up
p.2.critical
p.2.mutex.up
p.1.critical
p.1.mutex.up
0 1 2 3 4 5 6
Concurrency: monitors & condition synchronization 22
©Magee/Kramer 2nd Edition
semaphores in Java
public class Semaphore {
private int value;
public Semaphore (int initial)
{value = initial;}
synchronized public void up() {
++value;
notifyAll();
}
synchronized public void down()
throws InterruptedException {
while (value== 0) wait();
–value;
}
} Is it safe to use notify()
here rather than notifyAll()?
Semaphores are
passive objects,
therefore
implemented as
monitors.
(In practice,
semaphores are a
low-level mechanism
often used in
implementing the
higher-level monitor
construct.)
Concurrency: monitors & condition synchronization 23
©Magee/Kramer 2nd Edition
SEMADEMO display
current
semaphore
value
thread 1 is
executing
critical
actions.
thread 2 is
blocked
waiting.
thread 3 is
executing
non-critical
actions.
Concurrency: monitors & condition synchronization 24
©Magee/Kramer 2nd Edition
SEMADEMO
What if we adjust the time that each thread spends in its
critical section ?
♦large resource requirement – more conflict?
(eg. more than 67% of a rotation)?
♦ small resource requirement – no conflict?
(eg. less than 33% of a rotation)?
Hence the time a thread spends in its critical
section should be kept as short as possible.
Concurrency: monitors & condition synchronization 25
©Magee/Kramer 2nd Edition
SEMADEMO program – revised ThreadPanel class
public class ThreadPanel extends Panel {
// construct display with title and rotating arc color c
public ThreadPanel(String title, Color c) {…}
// hasSlider == true creates panel with slider
public ThreadPanel
(String title, Color c, boolean hasSlider) {…}
// rotate display of currently running thread 6 degrees
// return false when in initial color, return true when in second color
public static boolean rotate()
throws InterruptedException {…}
// rotate display of currently running thread by degrees
public static void rotate(int degrees)
throws InterruptedException {…}
// create a new thread with target r and start it running
public void start(Runnable r) {…}
// stop the thread using Thread.interrupt()
public void stop() {…}
}
Concurrency: monitors & condition synchronization 26
©Magee/Kramer 2nd Edition
SEMADEMO program – MutexLoop
class MutexLoop implements Runnable {
Semaphore mutex;
MutexLoop (Semaphore sema) {mutex=sema;}
public void run() {
try {
while(true) {
while(!ThreadPanel.rotate());
mutex.down(); // get mutual exclusion
while(ThreadPanel.rotate()); //critical actions
mutex.up(); //release mutual exclusion
}
} catch(InterruptedException e){}
}
} ThreadPanel.rotate() returns
false while executing non-critical
actions (dark color) and true otherwise.
Threads and
semaphore are
created by the
applet
start()
method.
Concurrency: monitors & condition synchronization 27
©Magee/Kramer 2nd Edition
5.3 Bounded Buffer
A bounded buffer consists of a fixed number of slots.
Items are put into the buffer by a producer process and
removed by a consumer process. It can be used to smooth
out transfer rates between the producer and consumer.
(see car park example)
Concurrency: monitors & condition synchronization 28
©Magee/Kramer 2nd Edition
bounded buffer – a data-independent model
PRODUCER BUFFER CONSUMERput get
BOUNDEDBUFFER
LTS:
The behaviour of BOUNDEDBUFFER is independent of
the actual data values, and so can be modelled in a data-
independent manner.
put put
get
put
get
put
get
put
get get
0 1 2 3 4 5
Concurrency: monitors & condition synchronization 29
©Magee/Kramer 2nd Edition
bounded buffer – a data-independent model
BUFFER(N=5) = COUNT[0],
COUNT[i:0..N]
= (when (i
|when (i>0) get->COUNT[i-1]
).
PRODUCER = (put->PRODUCER).
CONSUMER = (get->CONSUMER).
||BOUNDEDBUFFER =
(PRODUCER||BUFFER(5)||CONSUMER).
Concurrency: monitors & condition synchronization 30
©Magee/Kramer 2nd Edition
bounded buffer program – buffer monitor
public interface Buffer
class BufferImpl
…
public synchronized void put(E o)
throws InterruptedException {
while (count==size) wait();
buf[in] = o; ++count; in=(in+1)%size;
notifyAll();
}
public synchronized E get()
throws InterruptedException {
while (count==0) wait();
E o =buf[out];
buf[out]=null; –count; out=(out+1)%size;
notifyAll();
return (o);
}
}
We separate the
interface to
permit an
alternative
implementation
later.
Is it safe to use notify()
here rather than notifyAll()?
Concurrency: monitors & condition synchronization 31
©Magee/Kramer 2nd Edition
bounded buffer program – producer process
class Producer implements Runnable {
Buffer buf;
String alphabet= “abcdefghijklmnopqrstuvwxyz”;
Producer(Buffer b) {buf = b;}
public void run() {
try {
int ai = 0;
while(true) {
ThreadPanel.rotate(12);
buf.put(alphabet.charAt(ai));
ai=(ai+1) % alphabet.length();
ThreadPanel.rotate(348);
}
} catch (InterruptedException e){}
}
}
Similarly Consumer
which calls buf.get().
Concurrency: monitors & condition synchronization 32
©Magee/Kramer 2nd Edition
5.4 Nested Monitors
Suppose that, in place of using the count variable and condition
synchronization directly, we instead use two semaphores full and
empty to reflect the state of the buffer.
class SemaBuffer
…
Semaphore full; //counts number of items
Semaphore empty; //counts number of spaces
SemaBuffer(int size) {
this.size = size; buf =(E[])new Object[size];
full = new Semaphore(0);
empty= new Semaphore(size);
}
…
}
Concurrency: monitors & condition synchronization 33
©Magee/Kramer 2nd Edition
nested monitors – bounded buffer program
synchronized public void put(E o)
throws InterruptedException {
empty.down();
buf[in] = o;
++count; in=(in+1)%size;
full.up();
}
synchronized public E get()
throws InterruptedException{
full.down();
E o =buf[out]; buf[out]=null;
–count; out=(out+1)%size;
empty.up();
return (o);
}
Does this behave
as desired?
empty is decremented during a put operation, which is blocked
if empty is zero; full is decremented by a get operation, which
is blocked if full is zero.
Concurrency: monitors & condition synchronization 34
©Magee/Kramer 2nd Edition
nested monitors – bounded buffer model
const Max = 5
range Int = 0..Max
SEMAPHORE …as before…
BUFFER = (put -> empty.down ->full.up ->BUFFER
|get -> full.down ->empty.up ->BUFFER
).
PRODUCER = (put -> PRODUCER).
CONSUMER = (get -> CONSUMER).
||BOUNDEDBUFFER = (PRODUCER|| BUFFER || CONSUMER
||empty:SEMAPHORE(5)
||full:SEMAPHORE(0)
)@{put,get}. Does this behave
as desired?
Concurrency: monitors & condition synchronization 35
©Magee/Kramer 2nd Edition
nested monitors – bounded buffer model
LTSA analysis predicts a possible DEADLOCK:
Composing
potential DEADLOCK
States Composed: 28 Transitions: 32 in 60ms
Trace to DEADLOCK:
get
The Consumer tries to get a character, but the buffer is
empty. It blocks and releases the lock on the semaphore
full. The Producer tries to put a character into the
buffer, but also blocks. Why?
This situation is known as the nested monitor problem.
Concurrency: monitors & condition synchronization 36
©Magee/Kramer 2nd Edition
nested monitors – bounded buffer model
synchronized public Object get()
throws InterruptedException{
full.down(); // if no items, block!
…
}
full
empty
buffer
get down
waitbuffer
fullfull
put
Concurrency: monitors & condition synchronization 37
©Magee/Kramer 2nd Edition
nested monitors – revised bounded buffer program
The only way to avoid it in Java is by careful design. In this
example, the deadlock can be removed by ensuring that the monitor
lock for the buffer is not acquired until after semaphores are
decremented.
public void put(E o)
throws InterruptedException {
empty.down();
synchronized(this){
buf[in] = o; ++count; in=(in+1)%size;
}
full.up();
}
Concurrency: monitors & condition synchronization 38
©Magee/Kramer 2nd Edition
nested monitors – revised bounded buffer model
BUFFER = (put -> BUFFER
|get -> BUFFER
).
PRODUCER =(empty.down->put->full.up->PRODUCER).
CONSUMER =(full.down->get->empty.up->CONSUMER).
The semaphore actions have been moved to the producer
and consumer. This is exactly as in the implementation
where the semaphore actions are outside the monitor .
Does this behave as desired?
Minimized LTS?
Concurrency: monitors & condition synchronization 39
©Magee/Kramer 2nd Edition
5.5 Monitor invariants
An invariant for a monitor is an assertion concerning the variables
it encapsulates. This assertion must hold whenever there is no thread
executing inside the monitor i.e. on thread entry to and exit from a
monitor .
CarParkControl Invariant: 0 ≤ spaces ≤ N
Semaphore Invariant: 0 ≤ value
Buffer Invariant: 0 ≤ count ≤ size
and 0 ≤ in < size
and 0 ≤ out< size
and in = (out + count) modulo size
Invariants can be helpful in reasoning about correctness of monitors
using a logical proof-based approach. Generally we prefer to use a
model-based approach amenable to mechanical checking .
Concurrency: monitors & condition synchronization 40
©Magee/Kramer 2nd Edition
Summary
Concepts
monitors: encapsulated data + access procedures
mutual exclusion + condition synchronization
nested monitors
Model
guarded actions
Practice
private data and synchronized methods in Java
wait(), notify() and notifyAll() for condition synchronization
single thread active in the monitor at a time