A combination of data abstraction and mutual exclusion
Widely used in concurrent programming languages and libraries
Understand “classical” monitors
Understand Java monitors
A collection of encapsulated procedures
A single global lock to ensure mutex for all the operations in the monitor
Automatic mutex
Only one method from the monitor can run at a given time
A special type of variables called condition variables which are used for condition synchronisation
wait(c)
, andsignal(c)
The happy face can block on a conditional variable (it becomes a sad face)
It executes wait(c)
When a thread blocks (via wait(c)
), the monitor's lock is released
A bit like the semaphore operation acquire
(P)
wait(c)
blocks the executing thread on c
The blocked thread must release the mutex lock on the monitor
A bad face can become happy
It executes signal(c)
When a thread gets ready to run, it first tries to acquire the lock
A bit like the semaphore operation release
(V)
signal(c)
unblocks the first process blocked on c
What happens with the mutex ? It depends on the happy face!
There are different possible semantics describing what to do (we will see two of them)
The caller executes signal(c)
as the last command
It hands over the the mutex lock on the monitor to the unblocked thread
We will use the latest version for monitors (Java 5)
Locks are explicit
private final Lock lock = new ReentrantLock();
Conditional variables are declared based on the monitor lock
private final Condition toBeEmpty = lock.newCondition(); private final Condition toBeFull = lock.newCondition();
Why?
Code for buffer, producer, and consumer
Internal data structure
private E buf; private int elem = 0 ;
Writing into the buffer
public void put(E e) throws InterruptedException { lock.lock();if (elem == 1) toBeEmpty.await() ; // Wait to be empty
buf = e; elem++ ;
toBeFull.signal() ; lock.unlock(); }
Why the use of InterruptedException
?
Observe the use of lock
(always the same)
The conditional variables are used depending on the problem
There is another operation, signalAll()
, to awake all the blocked
threads in a given condition variable
Reading from the buffer
public E get() throws InterruptedException { E result ; lock.lock(); if (elem == 0) toBeFull.await() ; // Wait to be full! result = buf ; elem-- ; toBeEmpty.signal() ; lock.unlock(); return result; }
What about the producers and consumers?
We show it only for one producer and one consumer
Similar to the version with semaphores, only that we
tell the producer to use the class BufferMonOne
public ProducerM(BufferMonOneb, int p, int sp)
public ConsumerM(BufferMonOneb, int c, int sp)
Code for buffer, producer, and consumer
Remember how was the generalization for semaphores?
Two extra semaphores to protect front
and rear
Here, we do not need that. All the data structures to be thread-safe are inside the monitor
private E[] buf; private int elem = 0 ; private int S ; private int front ; private int rear ;
Writing into the buffer
public void put(E e) throws InterruptedException { lock.lock(); if (elem == S) toBeEmpty.await() ; buf[front] = e; front = (front+1)%S ; elem++ ; toBeFull.signal() ; lock.unlock(); }
Reading from the buffer
public E get() throws InterruptedException { E result ; lock.lock(); if (elem == 0) toBeFull.await() ; result = buf[rear] ; rear = (rear+1)%S ; elem-- ; toBeEmpty.signal() ; lock.unlock(); return result; }
Does it work the same as the semaphore version?
Semaphores
Monitors
Can monitors implement semaphores?
Can semaphores implement monitors?
Whatever you can write with semaphores, you can write it with monitors
So far we have looked at the Signal and Exit version of monitors
A signal is at the end of the method
Mutex is handed over to any woken process
Other possibilities
What if the signal is not at the end of the method?
What is the scheduling can be different?
Several possible semantics exist
Queues
The original proposal
await()
)Signal and wait
await()
)Signal and continue (used by Java!)
Pattern so far
One thread W is blocked on the conditional variable CV
if (cond) { CV.await() ; } // Rest of the code
One thread S, running in the monitor, executes
// Make cond false CV.signal() ; // Rest of the code
The condition cond
might change when the thread resumes execution after
await()
!
The condition might be accidentally modified by the signaling thread
public void put(E e) throws InterruptedException { lock.lock(); if (elem == S) toBeEmpty.await() ; // Rest of the code lock.unlock(); } public E get() throws InterruptedException { E result ; lock.lock(); if (elem == 0) toBeFull.await() ; result = buf[rear] ; rear = (rear+1)%S ; elem-- ; toBeEmpty.signal() ; // Programmer did an error here! if (elem == S-1) { rear = (rear-1)%S ; buf[rear] = result ; elem++ ; } lock.unlock(); return result; }
elem == S
The condition might be changed by a thread in the boundary queue ready to enter before the unblocked thread
Solution to this problem?
Rechecked the condition every time
We need to change our programming style
public void put(E e) throws InterruptedException { lock.lock(); if (elem == S) toBeEmpty.await() ; // Rest of the code lock.unlock(); } public E get() throws InterruptedException { E result ; lock.lock(); if (elem == 0) toBeFull.await() ; // Rest of the code lock.unlock(); return result; }
public void put(E e) throws InterruptedException { lock.lock(); while (elem == S) toBeEmpty.await() ; // Rest of the code lock.unlock(); } public E get() throws InterruptedException { E result ; lock.lock(); while (elem == 0) toBeFull.await() ; // Rest of the code lock.unlock(); return result; }
Is it that horrible this signal discipline?
Probably, but it is widely used (e.g. Unix, phthreads, Java)
Why?
Can we avoid the while-loops if we change the signaling discipline, e.g., signal and exit?
Not in Java! Can you guess why?
Spurious wake-ups
We have seen the exception InterruptedException
already
Method call thread.interrupt()
sets a flag known as interrupt status
It will immediately wake up the thread if it tries to block/sleep
Threads can check the flag by calling interrupted()
Difficult to use safely as a programming primitive
It can leave objects in hard-to-predict states
Your program should finish as part of its normal behavior
Nevertheless, it is very useful to finalize threads even tough they might be in a blocked state
public void run() { try { while (!interrupted()) //Do some work here } catch (InterruptedException e) {} } public void shutdown() throws InterruptedException { interrupt(); }
Here, another example that shows that interrupted threads are woken up if they were blocked
Java 5 monitor guarantee fairness for the boundary queue
Monitors have several queues that might interact
Boundary queue
Queues associated with synchronization variables
Could it be that fairness is not fulfill?
Yes!
A signal "can be stolen" from a blocked thread
An example
One thread waiting 10 years for a conditional to happen (blocked on
conditional variable c
)
One thread waiting 10 seconds to get into the monitor
The running thread makes that condition to become true and signals c
The blocked thread is added to the end of the boundary queue
What should we do about fairness?
It is generally ignored when using monitors
You can program your way around
Less fairness guarantees than in Java 5
No fairness for the boundary queue
Signals can be stolen (similar as monitors in Java 5)
Only one conditional variable per monitor
Common concurrency pattern
N processes must wait for the slowest before continuing with the next activity
Widely used in parallel programming
We will implement it as a monitor
public class Barrier { private final Lock lock = new ReentrantLock(); private final Condition AllOnTheBarrier = lock.newCondition(); private int members ; private int sofar ; public Barrier (int total) { sofar = 0 ; members = total ; } public void sync() throws InterruptedException { lock.lock(); sofar++ ; if (sofar == members) AllOnTheBarrier.signalAll() ; else while (sofar < members) AllOnTheBarrier.await() ; sofar-- ; lock.unlock(); } }
Does it work?
What is the problem?
We needed to add the while-loop to deal with spurious wake-ups
The while-loop might keep blocking threads when it times for them to leave the barrier
The last thread arrives to the barrier and signals to all the blocked thread
The thread that signaled all continues running and the first blocked
awaken thread executes sofar--
(exit the barrier)
sofar < members
(recall it was executed sofar--
), the other
threads to exit the barrier will block due to the line
while (sofar < members)
What if we do not use sofar--
?
public void sync() throws InterruptedException { lock.lock(); sofar++ ; if (sofar == members) { AllOnTheBarrier.signalAll() ; sofar = 0 ; } else while (sofar < members) AllOnTheBarrier.await() ; lock.unlock(); }
Solution?
Have an counter that says in which instance of the barrier is a thread blocked
For instance, it is in the first barrier, the second one, the third, etc?
A global variable which counts the current instance of the barrier (turn
)
and a local variable which stores to which instance the call to sync()
corresponds
public void sync() throws InterruptedException { int myturn ; // To which instance correspond this call lock.lock(); myturn = turn ; sofar++ ; if (sofar == members) { AllOnTheBarrier.signalAll() ; turn++ ; sofar = 0 ; } else while (sofar < members && turn == myturn) AllOnTheBarrier.await() ; lock.unlock(); }
Here, the code for the monitor, threads using it, and the top level file
What is turn overflows?
turn = (turn+1)%2
Allow better structured programming
As expressive as semaphores
Various monitor signaling semantics
Java monitors
Interrupting a (blocked) thread
Synchronization barrier
Next time
More classic problems
More Java monitors